TreeMap按值sorting

我想写一个比较,让我sortingTreeMap的价值,而不是默认的自然sorting。 我尝试了这样的事情,但无法找出问题所在:

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传递给比较?

你不能让TreeMap自己对值进行sorting,因为它违背了SortedMap规范:

一个Map ,进一步提供了关键的总sorting

但是,使用外部集合,无论您希望如何,您都可以通过键,值或者两者的组合(!!)对Map.entrySet()sorting。

下面是一个generics方法,返回一个Map.EntrySortedSet ,给定一个其值为ComparableMap

 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()那样的原始地图的“视图”。

一般来说,按照其值sorting地图条目的需求是非典型的。


== Integer注意事项

您的原始比较器使用==比较Integer 。 这几乎总是错误的,因为==Integer操作数是一个引用相等,而不是值相等。

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

相关问题

  • 在Java中比较两个整数是否会发生自动拆箱? (没有!!!)
  • 是否保证new Integer(i) == i在Java? (是!!!)

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”:哦!

代码的这种修改解决了这个问题:

 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)); 

一个TreeMap 总是按键sorting,其他的都是不可能的。 Comparator只允许您控制按键的sorting方式。

如果你想要sorting的值,你必须提取它们到一个List并对其进行sorting。

这不能通过使用Comparator来完成,因为它总是会获得要比较的地图的关键字TreeMap只能按键sorting。

奥洛夫的回答很好,但在完美之前还需要一件事情。 在他的回答下面的评论中,dacwe(正确地)指出他的实现违反了Sets的Equals / Equals合同。 如果您尝试调用包含或删除集合中明确的条目,则该集合将无法识别,因为允许将具有相同值的条目放在集合中的代码。 所以,为了解决这个问题,我们需要testing键之间的相等性:

 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; } 

“请注意,如果有序集合要正确实现Set接口,那么由有序集合(不论是否提供显式比较器)维护的顺序必须与equals一致… Set接口是根据等于操作,但是一个有序集合使用它的compareTo(或者compare)方法执行所有的元素比较,所以这个方法认为相等的两个元素从sorting集合的angular度来看是相等的 。 ( http://docs.oracle.com/javase/6/docs/api/java/util/SortedSet.html

由于我们最初忽略了平等,为了迫使这个集合添加等价的条目,现在我们必须testing关键字中的相等性,以便集合实际返回您正在查找的条目。 这有点乱,绝对不是如何使用集合 – 但它是有效的。

我知道这个post特别要求通过值对TreeMap进行sorting,但是对于我们这些并不关心实现的人来说,我们希望一个解决scheme能够在添加元素的时候保持sorting,所以我希望能够对这个基于TreeSet的反馈解。 首先,元素不容易通过键获取,但是对于我手边的用例(find具有最低值的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)); 

很多人听到build议使用列表,我更喜欢使用它

这里有两种方法需要根据它们的值对Map的条目进行sorting。

  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; }