Сортировка дерева значений



Я хочу написать компаратор, который позволит мне сортировать карту дерева по значению вместо естественного порядка по умолчанию.



Я пробовал что-то вроде этого, но не могу понять, что пошло не так:



import java.util.*;

class treeMap {
public static void main(String[] args) {
System.out.println("the main");
byValue cmp = new byValue();
Map<String, Integer> map = new TreeMap<String, Integer>(cmp);
map.put("de",10);
map.put("ab", 20);
map.put("a",5);

for (Map.Entry<String,Integer> pair: map.entrySet()) {
System.out.println(pair.getKey()+":"+pair.getValue());
}
}
}

class byValue implements Comparator<Map.Entry<String,Integer>> {
public int compare(Map.Entry<String,Integer> e1, Map.Entry<String,Integer> e2) {
if (e1.getValue() < e2.getValue()){
return 1;
} else if (e1.getValue() == e2.getValue()) {
return 0;
} else {
return -1;
}
}
}


Я думаю, что я спрашиваю: Могу ли я получить Map.Entry передается в компаратор?

418   8  

8 ответов:

вы не можете иметь TreeMap сама сортировка по значениям, так как это бросает вызов SortedMap спецификация:

A Map что дополнительно обеспечивает общий заказ на ключи.

однако, используя внешнюю коллекцию, вы всегда можете сортировать Map.entrySet() однако вы хотите, либо с помощью клавиш, значений или даже комбинации(!!) из двух.

вот общий метод что возвращает SortedSet на Map.Entry, предоставлена Map чьи значения Comparable:

static <K,V extends Comparable<? super V>>
SortedSet<Map.Entry<K,V>> entriesSortedByValues(Map<K,V> map) {
    SortedSet<Map.Entry<K,V>> sortedEntries = new TreeSet<Map.Entry<K,V>>(
        new Comparator<Map.Entry<K,V>>() {
            @Override public int compare(Map.Entry<K,V> e1, Map.Entry<K,V> e2) {
                int res = e1.getValue().compareTo(e2.getValue());
                return res != 0 ? res : 1;
            }
        }
    );
    sortedEntries.addAll(map.entrySet());
    return sortedEntries;
}

теперь вы можете сделать следующее:

    Map<String,Integer> map = new TreeMap<String,Integer>();
    map.put("A", 3);
    map.put("B", 2);
    map.put("C", 1);   

    System.out.println(map);
    // prints "{A=3, B=2, C=1}"
    System.out.println(entriesSortedByValues(map));
    // prints "[C=1, B=2, A=3]"

обратите внимание, что фанки вещи произойдет, если вы попытаетесь изменить или Map.Entry внутри, потому что это больше не "вид" исходной карты, как entrySet() есть.

вообще говоря, необходимость сортировки записей карты по ее значениям нетипична.


Примечание == ибо Integer

ваш оригинальный компаратор сравнивает Integer используя ==. Это почти всегда неправильно, так как == С Integer операнды-это равноправие, а не равенство значений.

    System.out.println(new Integer(0) == new Integer(0)); // prints "false"!!!

вопросы

polygenelubricants ответ почти идеальный. Однако у него есть одна важная ошибка. Он не будет обрабатывать записи карты, где значения одинаковы.

этот код:...

Map<String, Integer> nonSortedMap = new HashMap<String, Integer>();
nonSortedMap.put("ape", 1);
nonSortedMap.put("pig", 3);
nonSortedMap.put("cow", 1);
nonSortedMap.put("frog", 2);

for (Entry<String, Integer> entry  : entriesSortedByValues(nonSortedMap)) {
    System.out.println(entry.getKey()+":"+entry.getValue());
}

выводит:

ape:1
frog:2
pig:3

обратите внимание, как наша корова исчезла, поскольку она разделила значение " 1 " с нашей обезьяной :O!

эта модификация кода решает эту проблему:

static <K,V extends Comparable<? super V>> SortedSet<Map.Entry<K,V>> entriesSortedByValues(Map<K,V> map) {
        SortedSet<Map.Entry<K,V>> sortedEntries = new TreeSet<Map.Entry<K,V>>(
            new Comparator<Map.Entry<K,V>>() {
                @Override public int compare(Map.Entry<K,V> e1, Map.Entry<K,V> e2) {
                    int res = e1.getValue().compareTo(e2.getValue());
                    return res != 0 ? res : 1; // Special fix to preserve items with equal values
                }
            }
        );
        sortedEntries.addAll(map.entrySet());
        return sortedEntries;
    }

В Java 8:

LinkedHashMap<Integer, String> sortedMap = 
    map.entrySet().stream().
    sorted(Entry.comparingByValue()).
    collect(Collectors.toMap(Entry::getKey, Entry::getValue,
                             (e1, e2) -> e1, LinkedHashMap::new));

A TreeMap и всегда сортировка по ключам, все остальное невозможно. А Comparator просто позволяет вам контролировать как ключи сортируются.

Если вы хотите отсортировать значения, вы должны извлечь их в List и сортировать.

это не может быть сделано с помощью Comparator, Так как он всегда будет ключ карты для сравнения. TreeMap можно сортировать только по ключу.

ответ Олофа хорош, но ему нужно один больше вещей, прежде чем это идеально. В комментариях ниже его ответа dacwe (правильно) указывает, что его реализация нарушает контракт Compare/Equals для наборов. Если вы попытаетесь вызвать contains или remove для записи, которая явно находится в наборе, набор не распознает ее из-за кода, который позволяет помещать в набор записи с равными значениями. Итак, чтобы исправить это, нам нужно проверить равенство между ключи:

static <K,V extends Comparable<? super V>> SortedSet<Map.Entry<K,V>> entriesSortedByValues(Map<K,V> map) {
    SortedSet<Map.Entry<K,V>> sortedEntries = new TreeSet<Map.Entry<K,V>>(
        new Comparator<Map.Entry<K,V>>() {
            @Override public int compare(Map.Entry<K,V> e1, Map.Entry<K,V> e2) {
                int res = e1.getValue().compareTo(e2.getValue());
                if (e1.getKey().equals(e2.getKey())) {
                    return res; // Code will now handle equality properly
                } else {
                    return res != 0 ? res : 1; // While still adding all entries
                }
            }
        }
    );
    sortedEntries.addAll(map.entrySet());
    return sortedEntries;
}

" обратите внимание, что порядок, поддерживаемый отсортированным набором (независимо от того, предоставляется ли явный компаратор), должен быть согласован с equals, если сортированный набор должен правильно реализовать интерфейс набора... интерфейс Set определяется в терминах операции equals, но сортированный набор выполняет все сравнения элементов с помощью своего метода compareTo (или compare), поэтому два элемента, которые считаются равными этим методом, с точки зрения отсортированного набора, равный." (http://docs.oracle.com/javase/6/docs/api/java/util/SortedSet.html)

поскольку мы изначально упустили равенство, чтобы заставить набор добавлять равнозначные записи, теперь мы должны проверить равенство в ключах, чтобы набор фактически вернул запись, которую вы ищете. Это довольно грязно и определенно не так, как должны были использоваться наборы, но это работает.

Я знаю, что этот пост специально запрашивает сортировку TreeMap по значениям, но для тех из нас, кто действительно не заботится о реализации, но хочет получить решение, которое будет сортировать коллекцию по мере добавления элементов, я был бы признателен за обратную связь по этому решению на основе TreeSet. Во-первых, элементы не легко извлекаются ключом, но для случая использования, который у меня был под рукой (поиск N Ключей с самыми низкими значениями), это не было требованием.

  TreeSet<Map.Entry<Integer, Double>> set = new TreeSet<>(new Comparator<Map.Entry<Integer, Double>>()
  {
    @Override
    public int compare(Map.Entry<Integer, Double> o1, Map.Entry<Integer, Double> o2)
    {
      int valueComparison = o1.getValue().compareTo(o2.getValue());
      return valueComparison == 0 ? o1.getKey().compareTo(o2.getKey()) : valueComparison;
    }
  });
  int key = 5;
  double value = 1.0;
  set.add(new AbstractMap.SimpleEntry<>(key, value));

многие люди слышат adviced, чтобы использовать список, и я предпочитаю использовать его, а

вот два метода, которые вам нужно отсортировать записи карты в соответствии с их значениями.

    static final Comparator<Entry<?, Double>> DOUBLE_VALUE_COMPARATOR = 
        new Comparator<Entry<?, Double>>() {
            @Override
            public int compare(Entry<?, Double> o1, Entry<?, Double> o2) {
                return o1.getValue().compareTo(o2.getValue());
            }
        };

        static final List<Entry<?, Double>> sortHashMapByDoubleValue(HashMap temp)
        {
            Set<Entry<?, Double>> entryOfMap = temp.entrySet();

            List<Entry<?, Double>> entries = new ArrayList<Entry<?, Double>>(entryOfMap);
            Collections.sort(entries, DOUBLE_VALUE_COMPARATOR);
            return entries;
        }

Comments

    Ничего не найдено.