Вопрос-ответ

TreeMap sort by value

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

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

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

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, переданный компаратору?

Переведено автоматически
Ответ 1

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


Map Который дополнительно обеспечивает общий порядок для своих ключей.


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

Вот универсальный метод, который возвращает a SortedSet of Map.Entry, учитывая a, 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]"

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

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


Примечание о == для Integer

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

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

Вопросы по теме

Ответ 2

ответ 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;
}
Ответ 3

В Java 8:

LinkedHashMap<Integer, String> sortedMap = map.entrySet().stream()
.sorted(Map.Entry.comparingByValue(/* Optional: Comparator.reverseOrder() */))
.collect(Collectors.toMap(Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1, LinkedHashMap::new));
Ответ 4

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

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

java