按值(Java)对Map <Key,Value>进行sorting

我对Java相对来说比较陌生,经常发现我需要对值进行Map<Key, Value>sorting。 由于这些值不是唯一的,我发现自己将keySet转换为一个array ,并通过数组sorting使用自定义比较器 数组进行sorting ,该比较器对与键关联的值进行sorting。 有一个更简单的方法吗?

这是一个通用友好的版本,你可以自由使用:

 import java.util.*; public class MapUtil { public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) { List<Map.Entry<K, V>> list = new LinkedList<Map.Entry<K, V>>(map.entrySet()); Collections.sort( list, new Comparator<Map.Entry<K, V>>() { public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) { return (o1.getValue()).compareTo( o2.getValue() ); } }); Map<K, V> result = new LinkedHashMap<K, V>(); for (Map.Entry<K, V> entry : list) { result.put(entry.getKey(), entry.getValue()); } return result; } } 

和一个相关的JUnit4testing,所以你不必听我的话:

 import java.util.*; import org.junit.*; public class MapUtilTest { @Test public void testSortByValue() { Random random = new Random(System.currentTimeMillis()); Map<String, Integer> testMap = new HashMap<String, Integer>(1000); for(int i = 0; i < 1000; ++i) { testMap.put( "SomeString" + random.nextInt(), random.nextInt()); } testMap = MapUtil.sortByValue(testMap); Assert.assertEquals(1000, testMap.size()); Integer previous = null; for(Map.Entry<String, Integer> entry : testMap.entrySet()) { Assert.assertNotNull(entry.getValue()); if (previous != null) { Assert.assertTrue(entry.getValue() >= previous); } previous = entry.getValue(); } } } 

Java 7版本

 public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) { List<Map.Entry<K, V>> list = new LinkedList<>(map.entrySet()); Collections.sort( list, new Comparator<Map.Entry<K, V>>() { @Override public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) { return (o1.getValue()).compareTo(o2.getValue()); } }); Map<K, V> result = new LinkedHashMap<>(); for (Map.Entry<K, V> entry : list) { result.put(entry.getKey(), entry.getValue()); } return result; } 

Java 8版本。 这将按照升序排列的值进行sorting; 为了降序排列,可以取消对Collections.reverseOrder()的调用。

 public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) { return map.entrySet() .stream() .sorted(Map.Entry.comparingByValue(/*Collections.reverseOrder()*/)) .collect(Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new )); } 

还有一种技术是按值sortingHashMap。 这里没有使用比较器。 我们基于Keys进行sorting,交换键和值,根据值进行sorting,并再次交换以获取finalMap,该sorting是基于值对HashMap进行sorting的。

  private static LinkedHashMap<String, String> method1(HashMap<String, String> originalHashMap) { LinkedHashMap<String, String> sortedHashMapByKeys = new LinkedHashMap<>(); //maintains the order of putting TreeMap<String, String> originalTreeMap = new TreeMap<>(originalHashMap); //sorts based on keys for (Map.Entry<String, String> map: originalTreeMap.entrySet()) { sortedHashMapByKeys.put(map.getKey(), map.getValue()); } LinkedHashMap<String, String> reversedOfSortedLinkedHashMap = new LinkedHashMap<>(); for (Map.Entry<String, String> map: sortedHashMapByKeys.entrySet()) { reversedOfSortedLinkedHashMap.put(map.getValue(), map.getKey()); } LinkedHashMap<String, String> finalMap = new LinkedHashMap<>(); TreeMap<String, String> treeMapOfReversedOfSortedLinkedHashMap = new TreeMap<>(reversedOfSortedLinkedHashMap); for (Map.Entry<String, String> map: treeMapOfReversedOfSortedLinkedHashMap.entrySet()) { finalMap.put(map.getKey(), map.getValue()); //sort and swap } return finalMap; } 

重要的提示:

这个代码可以以多种方式打破。 如果您打算使用提供的代码,请务必阅读注释以了解其含义。 例如,值不能再通过键来检索。 (总是返回null 。)


这似乎比以上所有更容易。 使用一个TreeMap如下:

 public class Testing { public static void main(String[] args) { HashMap<String, Double> map = new HashMap<String, Double>(); ValueComparator bvc = new ValueComparator(map); TreeMap<String, Double> sorted_map = new TreeMap<String, Double>(bvc); map.put("A", 99.5); map.put("B", 67.4); map.put("C", 67.4); map.put("D", 67.3); System.out.println("unsorted map: " + map); sorted_map.putAll(map); System.out.println("results: " + sorted_map); } } class ValueComparator implements Comparator<String> { Map<String, Double> base; public ValueComparator(Map<String, Double> base) { this.base = base; } // Note: this comparator imposes orderings that are inconsistent with // equals. public int compare(String a, String b) { if (base.get(a) >= base.get(b)) { return -1; } else { return 1; } // returning 0 would merge keys } } 

输出:

 unsorted map: {D=67.3, A=99.5, B=67.4, C=67.4} results: {D=67.3, B=67.4, C=67.4, A=99.5} 

三个一行的答案…

我会使用Google Collections Guava来做到这一点 – 如果您的值是Comparable那么您可以使用

 valueComparator = Ordering.natural().onResultOf(Functions.forMap(map)) 

这将为地图创build一个函数(对象)[将任何键作为input,返回相应的值],然后对它们[值]应用自然(可比较的)sorting。

如果它们不具有可比性,那么就需要按照这个方法做一些事情

 valueComparator = Ordering.from(comparator).onResultOf(Functions.forMap(map)) 

这些可能被应用到TreeMap(作为Ordering extends Comparator ),或者一些sorting后的LinkedHashMap

注意 :如果要使用TreeMap,请记住,如果比较== 0,则该项目已经在列表中(如果您有多个比较相同的值,则会发生这种情况)。 为了减轻这一点,你可以把你的密钥加到比较器中(假设你的密钥和值是Comparable ):

 valueComparator = Ordering.natural().onResultOf(Functions.forMap(map)).compound(Ordering.natural()) 

= 对键映射的值应用自然sorting,并将其与键的自然sorting组合

请注意,如果您的键与0进行比较,则这仍然不起作用,但对于大多数comparable项目来说,这应该足够了(因为hashCodeequalscompareTo通常是同步的)

请参阅Ordering.onResultOf()和Functions.forMap() 。

履行

所以现在我们有了一个比较器来做我们想要的,我们需要从中得到一个结果。

 map = ImmutableSortedMap.copyOf(myOriginalMap, valueComparator); 

现在这很可能会工作,但是:

  1. 需要完成一个完整的地图
  2. 不要在TreeMap上尝试上面的比较器; 试图比较一个插入的键,当它没有一个值,直到放后,没有意义,也就是说,它会打破真的很快

对我来说,第一点对我来说有点不合适。 谷歌collections是非常懒惰的(这是好事:你可以在瞬间完成所有的操作;真正的工作是在开始使用结果的时候完成的),这就需要复制整个地图!

“完整”答案/按值sorting的地图

不要担心, 如果你对以这种方式sorting的“活”地图足够痴迷,那么你可以用下面这些疯狂的东西来解决上述问题中的一个,但不是这两个(!):

注意:这在2012年6月已经发生了很大变化 – 之前的代码无法工作:需要内部HashMap来查找值,而不会在TreeMap.get() – > compare()compare() – >之间创build无限循环get()

 import static org.junit.Assert.assertEquals; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; import com.google.common.base.Functions; import com.google.common.collect.Ordering; class ValueComparableMap<K extends Comparable<K>,V> extends TreeMap<K,V> { //A map for doing lookups on the keys for comparison so we don't get infinite loops private final Map<K, V> valueMap; ValueComparableMap(final Ordering<? super V> partialValueOrdering) { this(partialValueOrdering, new HashMap<K,V>()); } private ValueComparableMap(Ordering<? super V> partialValueOrdering, HashMap<K, V> valueMap) { super(partialValueOrdering //Apply the value ordering .onResultOf(Functions.forMap(valueMap)) //On the result of getting the value for the key from the map .compound(Ordering.natural())); //as well as ensuring that the keys don't get clobbered this.valueMap = valueMap; } public V put(K k, V v) { if (valueMap.containsKey(k)){ //remove the key in the sorted set before adding the key again remove(k); } valueMap.put(k,v); //To get "real" unsorted values for the comparator return super.put(k, v); //Put it in value order } public static void main(String[] args){ TreeMap<String, Integer> map = new ValueComparableMap<String, Integer>(Ordering.natural()); map.put("a", 5); map.put("b", 1); map.put("c", 3); assertEquals("b",map.firstKey()); assertEquals("a",map.lastKey()); map.put("d",0); assertEquals("d",map.firstKey()); //ensure it's still a map (by overwriting a key, but with a new value) map.put("d", 2); assertEquals("b", map.firstKey()); //Ensure multiple values do not clobber keys map.put("e", 2); assertEquals(5, map.size()); assertEquals(2, (int) map.get("e")); assertEquals(2, (int) map.get("d")); } } 

当我们放入时,我们确保哈希映射具有比较器的值,然后放到TreeSet进行sorting。 但在此之前,我们检查哈希映射以查看密钥实际上不是重复的。 此外,我们创build的比较器还将包含密钥,以便重复值不会删除非重复密钥(由于==比较)。 这两个项目对于确保地图合同保持至关重要 。 如果你认为你不想要的话,那么你几乎要完全颠倒地图( Map<V,K> )。

构造函数需要被调用

  new ValueComparableMap(Ordering.natural()); //or new ValueComparableMap(Ordering.from(comparator)); 

http://www.programmersheaven.com/download/49349/download.aspx

 private static <K, V> Map<K, V> sortByValue(Map<K, V> map) { List<Entry<K, V>> list = new LinkedList<>(map.entrySet()); Collections.sort(list, new Comparator<Object>() { @SuppressWarnings("unchecked") public int compare(Object o1, Object o2) { return ((Comparable<V>) ((Map.Entry<K, V>) (o1)).getValue()).compareTo(((Map.Entry<K, V>) (o2)).getValue()); } }); Map<K, V> result = new LinkedHashMap<>(); for (Iterator<Entry<K, V>> it = list.iterator(); it.hasNext();) { Map.Entry<K, V> entry = (Map.Entry<K, V>) it.next(); result.put(entry.getKey(), entry.getValue()); } return result; } 

Java 8提供了一个新的答案:将条目转换为stream,并使用Map.Entry中的比较组合器:

 Stream<Map.Entry<K,V>> sorted = map.entrySet().stream() .sorted(Map.Entry.comparingByValue()); 

这样可以让您使用按值升序sorting的条目。 如果你想降低价值,只需扭转比较:

 Stream<Map.Entry<K,V>> sorted = map.entrySet().stream() .sorted(Collections.reverseOrder(Map.Entry.comparingByValue())); 

如果这些值不具有可比性,则可以传递一个明确的比较器:

 Stream<Map.Entry<K,V>> sorted = map.entrySet().stream() .sorted(Map.Entry.comparingByValue(comparator)); 

然后您可以继续使用其他stream操作来使用数据。 例如,如果你想在新地图中排名前10位:

 Map<K,V> topTen = map.entrySet().stream() .sorted(Map.Entry.comparingByKey(Comparator.reverseOrder())) .limit(10) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); 

或者打印到System.out

 map.entrySet().stream() .sorted(Map.Entry.comparingByValue()) .forEach(System.out::println); 

对键进行sorting需要比较器为每个比较查找每个值。 一个更具可扩展性的解决scheme将直接使用entrySet,因为这个值将立即可用于每个比较(尽pipe我还没有用数字来支持)。

这是一个这样的事情的通用版本:

 public static <K, V extends Comparable<? super V>> List<K> getKeysSortedByValue(Map<K, V> map) { final int size = map.size(); final List<Map.Entry<K, V>> list = new ArrayList<Map.Entry<K, V>>(size); list.addAll(map.entrySet()); final ValueComparator<V> cmp = new ValueComparator<V>(); Collections.sort(list, cmp); final List<K> keys = new ArrayList<K>(size); for (int i = 0; i < size; i++) { keys.set(i, list.get(i).getKey()); } return keys; } private static final class ValueComparator<V extends Comparable<? super V>> implements Comparator<Map.Entry<?, V>> { public int compare(Map.Entry<?, V> o1, Map.Entry<?, V> o2) { return o1.getValue().compareTo(o2.getValue()); } } 

有办法减less上述解决scheme的内存旋转。 创build的第一个ArrayList可以重新用作返回值; 这需要抑制一些generics警告,但对于可重用的库代码来说,这可能是值得的。 另外,比较器不必在每次调用时重新分配。

这是一个更有效率,尽pipe不太吸引人的版本:

 public static <K, V extends Comparable<? super V>> List<K> getKeysSortedByValue2(Map<K, V> map) { final int size = map.size(); final List reusedList = new ArrayList(size); final List<Map.Entry<K, V>> meView = reusedList; meView.addAll(map.entrySet()); Collections.sort(meView, SINGLE); final List<K> keyView = reusedList; for (int i = 0; i < size; i++) { keyView.set(i, meView.get(i).getKey()); } return keyView; } private static final Comparator SINGLE = new ValueComparator(); 

最后,如果您需要连续访问已sorting的信息(而不是仅仅偶尔sorting一次),则可以使用额外的多地图。 让我知道如果你需要更多的细节…

commons-collections库包含一个名为TreeBidiMap的解决scheme。 或者,您可以查看Google Collections API。 它有你可以使用的TreeMultimap 。

如果你不想使用这些框架…他们来源代码。

在Java 8中,您可以使用api API以非常简单的方式进行操作:

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

我已经查看了给定的答案,但是其中的很多元素比需要的更复杂,或者当几个键具有相同的值时移除地图元素。

这是一个我认为更合适的解决scheme:

 public static <K, V extends Comparable<V>> Map<K, V> sortByValues(final Map<K, V> map) { Comparator<K> valueComparator = new Comparator<K>() { public int compare(K k1, K k2) { int compare = map.get(k2).compareTo(map.get(k1)); if (compare == 0) return 1; else return compare; } }; Map<K, V> sortedByValues = new TreeMap<K, V>(valueComparator); sortedByValues.putAll(map); return sortedByValues; } 

请注意,地图是从最高值到最低值sorting的。

用Java 8的新特性来完成这个任务:

 import static java.util.Map.Entry.comparingByValue; import static java.util.stream.Collectors.toList; <K, V> List<Entry<K, V>> sort(Map<K, V> map, Comparator<? super V> comparator) { return map.entrySet().stream().sorted(comparingByValue(comparator)).collect(toList()); } 

使用给定的比较器对条目进行sorting。 或者,如果您的值可以相互比较,则不需要显式比较器:

 <K, V extends Comparable<? super V>> List<Entry<K, V>> sort(Map<K, V> map) { return map.entrySet().stream().sorted(comparingByValue()).collect(toList()); } 

返回的列表是在调用此方法时给定映射的快照,因此两者都不会反映随后的更改。 对于地图的实时迭代视图:

 <K, V extends Comparable<? super V>> Iterable<Entry<K, V>> sort(Map<K, V> map) { return () -> map.entrySet().stream().sorted(comparingByValue()).iterator(); } 

返回的迭代器在每次迭代时都会为给定的地图创build一个新的快照,因此,如果不进行并发修改,它将始终反映地图的当前状态。

创build自定义的比较器并在创build新的TreeMap对象时使用它。

 class MyComparator implements Comparator<Object> { Map<String, Integer> map; public MyComparator(Map<String, Integer> map) { this.map = map; } public int compare(Object o1, Object o2) { if (map.get(o2) == map.get(o1)) return 1; else return ((Integer) map.get(o2)).compareTo((Integer) map.get(o1)); } } 

在你的主要function中使用下面的代码

  Map<String, Integer> lMap = new HashMap<String, Integer>(); lMap.put("A", 35); lMap.put("B", 75); lMap.put("C", 50); lMap.put("D", 50); MyComparator comparator = new MyComparator(lMap); Map<String, Integer> newMap = new TreeMap<String, Integer>(comparator); newMap.putAll(lMap); System.out.println(newMap); 

输出:

 {B=75, D=50, C=50, A=35} 

虽然我同意不断需要sorting映射可能是一种气味,我认为下面的代码是最简单的方法来做到这一点,而不使用不同的数据结构。

 public class MapUtilities { public static <K, V extends Comparable<V>> List<Entry<K, V>> sortByValue(Map<K, V> map) { List<Entry<K, V>> entries = new ArrayList<Entry<K, V>>(map.entrySet()); Collections.sort(entries, new ByValue<K, V>()); return entries; } private static class ByValue<K, V extends Comparable<V>> implements Comparator<Entry<K, V>> { public int compare(Entry<K, V> o1, Entry<K, V> o2) { return o1.getValue().compareTo(o2.getValue()); } } 

}

这是一个令人尴尬的不完整的unit testing:

 public class MapUtilitiesTest extends TestCase { public void testSorting() { HashMap<String, Integer> map = new HashMap<String, Integer>(); map.put("One", 1); map.put("Two", 2); map.put("Three", 3); List<Map.Entry<String, Integer>> sorted = MapUtilities.sortByValue(map); assertEquals("First", "One", sorted.get(0).getKey()); assertEquals("Second", "Two", sorted.get(1).getKey()); assertEquals("Third", "Three", sorted.get(2).getKey()); } 

}

结果是一个Map.Entry对象的sorting列表,您可以从中获取这些键和值。

使用通用的比较器,如:

 final class MapValueComparator<K,V extends Comparable<V>> implements Comparator<K> { private Map<K,V> map; private MapValueComparator() { super(); } public MapValueComparator(Map<K,V> map) { this(); this.map = map; } public int compare(K o1, K o2) { return map.get(o1).compareTo(map.get(o2)); } } 

当你有2个项目等于时,答案最多的答案是行不通的。 TreeMap离开相等的值。

例如:未sorting的地图

键/值:D / 67.3
键/值:A / 99.5
键/值:B / 67.4
键/值:C / 67.5
键/值:E / 99.5

结果

键/值:A / 99.5
键/值:C / 67.5
键/值:B / 67.4
键/值:D / 67.3

所以留下E!

对我来说,它调整比较器,如果它等于不返回0,但-1。

在这个例子中:

类ValueComparator实现比较器{

地图基地; 公共ValueComparator(映射基){this.base = base; }

public int compare(Object a,Object b){

 if((Double)base.get(a) < (Double)base.get(b)) { return 1; } else if((Double)base.get(a) == (Double)base.get(b)) { return -1; } else { return -1; } 

}}

现在它返回:

未sorting的地图:

键/值:D / 67.3
键/值:A / 99.5
键/值:B / 67.4
键/值:C / 67.5
键/值:E / 99.5

结果:

键/值:A / 99.5
键/值:E / 99.5
键/值:C / 67.5
键/值:B / 67.4
键/值:D / 67.3

作为对外星人的回应(2011年11月22日):我正在使用这个解决scheme来绘制一个Integer Id的名字和地图,但是这个想法是一样的,所以可能是上面的代码是不正确的(我会在testing中写它并给你正确的代码),这是基于上述解决scheme的Mapsorting代码:

 package nl.iamit.util; import java.util.Comparator; import java.util.Map; public class Comparators { public static class MapIntegerStringComparator implements Comparator { Map<Integer, String> base; public MapIntegerStringComparator(Map<Integer, String> base) { this.base = base; } public int compare(Object a, Object b) { int compare = ((String) base.get(a)) .compareTo((String) base.get(b)); if (compare == 0) { return -1; } return compare; } } } 

and this is the test class (I just tested it, and this works for the Integer, String Map:

 package test.nl.iamit.util; import java.util.HashMap; import java.util.TreeMap; import nl.iamit.util.Comparators; import org.junit.Test; import static org.junit.Assert.assertArrayEquals; public class TestComparators { @Test public void testMapIntegerStringComparator(){ HashMap<Integer, String> unSoretedMap = new HashMap<Integer, String>(); Comparators.MapIntegerStringComparator bvc = new Comparators.MapIntegerStringComparator( unSoretedMap); TreeMap<Integer, String> sorted_map = new TreeMap<Integer, String>(bvc); //the testdata: unSoretedMap.put(new Integer(1), "E"); unSoretedMap.put(new Integer(2), "A"); unSoretedMap.put(new Integer(3), "E"); unSoretedMap.put(new Integer(4), "B"); unSoretedMap.put(new Integer(5), "F"); sorted_map.putAll(unSoretedMap); Object[] targetKeys={new Integer(2),new Integer(4),new Integer(3),new Integer(1),new Integer(5) }; Object[] currecntKeys=sorted_map.keySet().toArray(); assertArrayEquals(targetKeys,currecntKeys); } } 

here is the code for the Comparator of a Map:

 public static class MapStringDoubleComparator implements Comparator { Map<String, Double> base; public MapStringDoubleComparator(Map<String, Double> base) { this.base = base; } //note if you want decending in stead of ascending, turn around 1 and -1 public int compare(Object a, Object b) { if ((Double) base.get(a) == (Double) base.get(b)) { return 0; } else if((Double) base.get(a) < (Double) base.get(b)) { return -1; }else{ return 1; } } } 

and this is the testcase for this:

 @Test public void testMapStringDoubleComparator(){ HashMap<String, Double> unSoretedMap = new HashMap<String, Double>(); Comparators.MapStringDoubleComparator bvc = new Comparators.MapStringDoubleComparator( unSoretedMap); TreeMap<String, Double> sorted_map = new TreeMap<String, Double>(bvc); //the testdata: unSoretedMap.put("D",new Double(67.3)); unSoretedMap.put("A",new Double(99.5)); unSoretedMap.put("B",new Double(67.4)); unSoretedMap.put("C",new Double(67.5)); unSoretedMap.put("E",new Double(99.5)); sorted_map.putAll(unSoretedMap); Object[] targetKeys={"D","B","C","E","A"}; Object[] currecntKeys=sorted_map.keySet().toArray(); assertArrayEquals(targetKeys,currecntKeys); } 

of cource you can make this a lot more generic, but I just needed it for 1 case (the Map)

Instead of using Collections.sort as some do I'd suggest using Arrays.sort . Actually what Collections.sort does is something like this:

 public static <T extends Comparable<? super T>> void sort(List<T> list) { Object[] a = list.toArray(); Arrays.sort(a); ListIterator<T> i = list.listIterator(); for (int j=0; j<a.length; j++) { i.next(); i.set((T)a[j]); } } 

It just calls toArray on the list and then uses Arrays.sort . This way all the map entries will be copied three times: once from the map to the temporary list (be it a LinkedList or ArrayList), then to the temporary array and finally to the new map.

My solution ommits this one step as it does not create unnecessary LinkedList. Here is the code, generic-friendly and performance-optimal:

 public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) { @SuppressWarnings("unchecked") Map.Entry<K,V>[] array = map.entrySet().toArray(new Map.Entry[map.size()]); Arrays.sort(array, new Comparator<Map.Entry<K, V>>() { public int compare(Map.Entry<K, V> e1, Map.Entry<K, V> e2) { return e1.getValue().compareTo(e2.getValue()); } }); Map<K, V> result = new LinkedHashMap<K, V>(); for (Map.Entry<K, V> entry : array) result.put(entry.getKey(), entry.getValue()); return result; } 

This is a variation of Anthony's answer, which doesn't work if there are duplicate values:

 public static <K, V extends Comparable<V>> Map<K, V> sortMapByValues(final Map<K, V> map) { Comparator<K> valueComparator = new Comparator<K>() { public int compare(K k1, K k2) { final V v1 = map.get(k1); final V v2 = map.get(k2); /* Not sure how to handle nulls ... */ if (v1 == null) { return (v2 == null) ? 0 : 1; } int compare = v2.compareTo(v1); if (compare != 0) { return compare; } else { Integer h1 = k1.hashCode(); Integer h2 = k2.hashCode(); return h2.compareTo(h1); } } }; Map<K, V> sortedByValues = new TreeMap<K, V>(valueComparator); sortedByValues.putAll(map); return sortedByValues; } 

Note that it's rather up in the air how to handle nulls.

One important advantage of this approach is that it actually returns a Map, unlike some of the other solutions offered here.

Major problem. If you use the first answer (Google takes you here), change the comparator to add an equal clause, otherwise you cannot get values from the sorted_map by keys:

 public int compare(String a, String b) { if (base.get(a) > base.get(b)) { return 1; } else if (base.get(a) < base.get(b)){ return -1; } return 0; // returning 0 would merge keys } 

There are a lot of answers for this question already, but none provided me what I was looking for, a map implementation that returns keys and entries sorted by the associated value, and maintains this property as keys and values are modified in the map. Two other questions ask for this specifically.

I cooked up a generic friendly example that solves this use case. This implementation does not honor all of the contracts of the Map interface, such as reflecting value changes and removals in the sets return from keySet() and entrySet() in the original object. I felt such a solution would be too large to include in a Stack Overflow answer. If I manage to create a more complete implementation, perhaps I will post it to Github and then to it link in an updated version of this answer.

 import java.util.*; /** * A map where {@link #keySet()} and {@link #entrySet()} return sets ordered * by associated values based on the the comparator provided at construction * time. The order of two or more keys with identical values is not defined. * <p> * Several contracts of the Map interface are not satisfied by this minimal * implementation. */ public class ValueSortedMap<K, V> extends HashMap<K, V> { protected Map<V, Collection<K>> valueToKeysMap; // uses natural order of value object, if any public ValueSortedMap() { this((Comparator<? super V>) null); } public ValueSortedMap(Comparator<? super V> valueComparator) { this.valueToKeysMap = new TreeMap<V, Collection<K>>(valueComparator); } public boolean containsValue(Object o) { return valueToKeysMap.containsKey(o); } public V put(K k, V v) { V oldV = null; if (containsKey(k)) { oldV = get(k); valueToKeysMap.get(oldV).remove(k); } super.put(k, v); if (!valueToKeysMap.containsKey(v)) { Collection<K> keys = new ArrayList<K>(); keys.add(k); valueToKeysMap.put(v, keys); } else { valueToKeysMap.get(v).add(k); } return oldV; } public void putAll(Map<? extends K, ? extends V> m) { for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) put(e.getKey(), e.getValue()); } public V remove(Object k) { V oldV = null; if (containsKey(k)) { oldV = get(k); super.remove(k); valueToKeysMap.get(oldV).remove(k); } return oldV; } public void clear() { super.clear(); valueToKeysMap.clear(); } public Set<K> keySet() { LinkedHashSet<K> ret = new LinkedHashSet<K>(size()); for (V v : valueToKeysMap.keySet()) { Collection<K> keys = valueToKeysMap.get(v); ret.addAll(keys); } return ret; } public Set<Map.Entry<K, V>> entrySet() { LinkedHashSet<Map.Entry<K, V>> ret = new LinkedHashSet<Map.Entry<K, V>>(size()); for (Collection<K> keys : valueToKeysMap.values()) { for (final K k : keys) { final V v = get(k); ret.add(new Map.Entry<K,V>() { public K getKey() { return k; } public V getValue() { return v; } public V setValue(V v) { throw new UnsupportedOperationException(); } }); } } return ret; } } 

This is just too complicated. Maps were not supposed to do such job as sorting them by Value. The easiest way is to create your own Class so it fits your requirement.

In example lower you are supposed to add TreeMap a comparator at place where * is. But by java API it gives comparator only keys, not values. All of examples stated here is based on 2 Maps. One Hash and one new Tree. Which is odd.

例子:

 Map<Driver driver, Float time> map = new TreeMap<Driver driver, Float time>(*); 

So change the map into a set this way:

 ResultComparator rc = new ResultComparator(); Set<Results> set = new TreeSet<Results>(rc); 

You will create class Results ,

 public class Results { private Driver driver; private Float time; public Results(Driver driver, Float time) { this.driver = driver; this.time = time; } public Float getTime() { return time; } public void setTime(Float time) { this.time = time; } public Driver getDriver() { return driver; } public void setDriver (Driver driver) { this.driver = driver; } } 

and the Comparator class:

 public class ResultsComparator implements Comparator<Results> { public int compare(Results t, Results t1) { if (t.getTime() < t1.getTime()) { return 1; } else if (t.getTime() == t1.getTime()) { return 0; } else { return -1; } } } 

This way you can easily add more dependencies.

And as the last point I'll add simple iterator:

 Iterator it = set.iterator(); while (it.hasNext()) { Results r = (Results)it.next(); System.out.println( r.getDriver().toString //or whatever that is related to Driver class -getName() getSurname() + " " + r.getTime() ); } 

Best Approach

 import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Map.Entry; public class OrderByValue { public static void main(String a[]){ Map<String, Integer> map = new HashMap<String, Integer>(); map.put("java", 20); map.put("C++", 45); map.put("Unix", 67); map.put("MAC", 26); map.put("Why this kolavari", 93); Set<Entry<String, Integer>> set = map.entrySet(); List<Entry<String, Integer>> list = new ArrayList<Entry<String, Integer>>(set); Collections.sort( list, new Comparator<Map.Entry<String, Integer>>() { public int compare( Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2 ) { return (o1.getValue()).compareTo( o2.getValue() );//Ascending order //return (o2.getValue()).compareTo( o1.getValue() );//Descending order } } ); for(Map.Entry<String, Integer> entry:list){ System.out.println(entry.getKey()+" ==== "+entry.getValue()); } }} 

产量

 java ==== 20 MAC ==== 26 C++ ==== 45 Unix ==== 67 Why this kolavari ==== 93 

Based on @devinmoore code, a map sorting methods using generics and supporting both ascending and descending ordering.

 /** * Sort a map by it's keys in ascending order. * * @return new instance of {@link LinkedHashMap} contained sorted entries of supplied map. * @author Maxim Veksler */ public static <K, V> LinkedHashMap<K, V> sortMapByKey(final Map<K, V> map) { return sortMapByKey(map, SortingOrder.ASCENDING); } /** * Sort a map by it's values in ascending order. * * @return new instance of {@link LinkedHashMap} contained sorted entries of supplied map. * @author Maxim Veksler */ public static <K, V> LinkedHashMap<K, V> sortMapByValue(final Map<K, V> map) { return sortMapByValue(map, SortingOrder.ASCENDING); } /** * Sort a map by it's keys. * * @param sortingOrder {@link SortingOrder} enum specifying requested sorting order. * @return new instance of {@link LinkedHashMap} contained sorted entries of supplied map. * @author Maxim Veksler */ public static <K, V> LinkedHashMap<K, V> sortMapByKey(final Map<K, V> map, final SortingOrder sortingOrder) { Comparator<Map.Entry<K, V>> comparator = new Comparator<Entry<K,V>>() { public int compare(Entry<K, V> o1, Entry<K, V> o2) { return comparableCompare(o1.getKey(), o2.getKey(), sortingOrder); } }; return sortMap(map, comparator); } /** * Sort a map by it's values. * * @param sortingOrder {@link SortingOrder} enum specifying requested sorting order. * @return new instance of {@link LinkedHashMap} contained sorted entries of supplied map. * @author Maxim Veksler */ public static <K, V> LinkedHashMap<K, V> sortMapByValue(final Map<K, V> map, final SortingOrder sortingOrder) { Comparator<Map.Entry<K, V>> comparator = new Comparator<Entry<K,V>>() { public int compare(Entry<K, V> o1, Entry<K, V> o2) { return comparableCompare(o1.getValue(), o2.getValue(), sortingOrder); } }; return sortMap(map, comparator); } @SuppressWarnings("unchecked") private static <T> int comparableCompare(T o1, T o2, SortingOrder sortingOrder) { int compare = ((Comparable<T>)o1).compareTo(o2); switch (sortingOrder) { case ASCENDING: return compare; case DESCENDING: return (-1) * compare; } return 0; } /** * Sort a map by supplied comparator logic. * * @return new instance of {@link LinkedHashMap} contained sorted entries of supplied map. * @author Maxim Veksler */ public static <K, V> LinkedHashMap<K, V> sortMap(final Map<K, V> map, final Comparator<Map.Entry<K, V>> comparator) { // Convert the map into a list of key,value pairs. List<Map.Entry<K, V>> mapEntries = new LinkedList<Map.Entry<K, V>>(map.entrySet()); // Sort the converted list according to supplied comparator. Collections.sort(mapEntries, comparator); // Build a new ordered map, containing the same entries as the old map. LinkedHashMap<K, V> result = new LinkedHashMap<K, V>(map.size() + (map.size() / 20)); for(Map.Entry<K, V> entry : mapEntries) { // We iterate on the mapEntries list which is sorted by the comparator putting new entries into // the targeted result which is a sorted map. result.put(entry.getKey(), entry.getValue()); } return result; } /** * Sorting order enum, specifying request result sort behavior. * @author Maxim Veksler * */ public static enum SortingOrder { /** * Resulting sort will be from smaller to biggest. */ ASCENDING, /** * Resulting sort will be from biggest to smallest. */ DESCENDING } 

Here is an OO solution (ie, doesn't use static methods):

 import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; public class SortableValueMap<K, V extends Comparable<V>> extends LinkedHashMap<K, V> { public SortableValueMap() { } public SortableValueMap( Map<K, V> map ) { super( map ); } public void sortByValue() { List<Map.Entry<K, V>> list = new LinkedList<Map.Entry<K, V>>( entrySet() ); Collections.sort( list, new Comparator<Map.Entry<K, V>>() { public int compare( Map.Entry<K, V> entry1, Map.Entry<K, V> entry2 ) { return entry1.getValue().compareTo( entry2.getValue() ); } }); clear(); for( Map.Entry<K, V> entry : list ) { put( entry.getKey(), entry.getValue() ); } } private static void print( String text, Map<String, Double> map ) { System.out.println( text ); for( String key : map.keySet() ) { System.out.println( "key/value: " + key + "/" + map.get( key ) ); } } public static void main( String[] args ) { SortableValueMap<String, Double> map = new SortableValueMap<String, Double>(); map.put( "A", 67.5 ); map.put( "B", 99.5 ); map.put( "C", 82.4 ); map.put( "D", 42.0 ); print( "Unsorted map", map ); map.sortByValue(); print( "Sorted map", map ); } } 

Hereby donated to the public domain.

Afaik the most cleaner way is utilizing collections to sort map on value:

 Map<String, Long> map = new HashMap<String, Long>(); // populate with data to sort on Value // use datastructure designed for sorting Queue queue = new PriorityQueue( map.size(), new MapComparable() ); queue.addAll( map.entrySet() ); // get a sorted map LinkedHashMap<String, Long> linkedMap = new LinkedHashMap<String, Long>(); for (Map.Entry<String, Long> entry; (entry = queue.poll())!=null;) { linkedMap.put(entry.getKey(), entry.getValue()); } public static class MapComparable implements Comparator<Map.Entry<String, Long>>{ public int compare(Entry<String, Long> e1, Entry<String, Long> e2) { return e1.getValue().compareTo(e2.getValue()); } } 

Since TreeMap<> does not work for values that can be equal, I used this:

 private <K, V extends Comparable<? super V>> List<Entry<K, V>> sort(Map<K, V> map) { List<Map.Entry<K, V>> list = new LinkedList<Map.Entry<K, V>>(map.entrySet()); Collections.sort(list, new Comparator<Map.Entry<K, V>>() { public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) { return o1.getValue().compareTo(o2.getValue()); } }); return list; } 

You might want to put list in a LinkedHashMap , but if you're only going to iterate over it right away, that's superfluous…

Some simple changes in order to have a sorted map with pairs that have duplicate values. In the compare method (class ValueComparator) when values are equal do not return 0 but return the result of comparing the 2 keys. Keys are distinct in a map so you succeed to keep duplicate values (which are sorted by keys by the way). So the above example could be modified like this:

  public int compare(Object a, Object b) { if((Double)base.get(a) < (Double)base.get(b)) { return 1; } else if((Double)base.get(a) == (Double)base.get(b)) { return ((String)a).compareTo((String)b); } else { return -1; } } } 

For sure the solution of Stephen is really great, but for those who can't use Guava:

Here's my solution for sorting by value a map. This solution handle the case where there are twice the same value etc…

 // If you want to sort a map by value, and if there can be twice the same value: // here is your original map Map<String,Integer> mapToSortByValue = new HashMap<String, Integer>(); mapToSortByValue.put("A", 3); mapToSortByValue.put("B", 1); mapToSortByValue.put("C", 3); mapToSortByValue.put("D", 5); mapToSortByValue.put("E", -1); mapToSortByValue.put("F", 1000); mapToSortByValue.put("G", 79); mapToSortByValue.put("H", 15); // Sort all the map entries by value Set<Map.Entry<String,Integer>> set = new TreeSet<Map.Entry<String,Integer>>( new Comparator<Map.Entry<String,Integer>>(){ @Override public int compare(Map.Entry<String,Integer> obj1, Map.Entry<String,Integer> obj2) { Integer val1 = obj1.getValue(); Integer val2 = obj2.getValue(); // DUPLICATE VALUE CASE // If the values are equals, we can't return 0 because the 2 entries would be considered // as equals and one of them would be deleted (because we use a set, no duplicate, remember!) int compareValues = val1.compareTo(val2); if ( compareValues == 0 ) { String key1 = obj1.getKey(); String key2 = obj2.getKey(); int compareKeys = key1.compareTo(key2); if ( compareKeys == 0 ) { // what you return here will tell us if you keep REAL KEY-VALUE duplicates in your set // if you want to, do whatever you want but do not return 0 (but don't break the comparator contract!) return 0; } return compareKeys; } return compareValues; } } ); set.addAll(mapToSortByValue.entrySet()); // OK NOW OUR SET IS SORTED COOL!!!! // And there's nothing more to do: the entries are sorted by value! for ( Map.Entry<String,Integer> entry : set ) { System.out.println("Set entries: " + entry.getKey() + " -> " + entry.getValue()); } // But if you add them to an hashmap Map<String,Integer> myMap = new HashMap<String,Integer>(); // When iterating over the set the order is still good in the println... for ( Map.Entry<String,Integer> entry : set ) { System.out.println("Added to result map entries: " + entry.getKey() + " " + entry.getValue()); myMap.put(entry.getKey(), entry.getValue()); } // But once they are in the hashmap, the order is not kept! for ( Integer value : myMap.values() ) { System.out.println("Result map values: " + value); } // Also this way doesn't work: // Logic because the entryset is a hashset for hashmaps and not a treeset // (and even if it was a treeset, it would be on the keys only) for ( Map.Entry<String,Integer> entry : myMap.entrySet() ) { System.out.println("Result map entries: " + entry.getKey() + " -> " + entry.getValue()); } // CONCLUSION: // If you want to iterate on a map ordered by value, you need to remember: // 1) Maps are only sorted by keys, so you can't sort them directly by value // 2) So you simply CAN'T return a map to a sortMapByValue function // 3) You can't reverse the keys and the values because you have duplicate values // This also means you can't neither use Guava/Commons bidirectionnal treemaps or stuff like that // SOLUTIONS // So you can: // 1) only sort the values which is easy, but you loose the key/value link (since you have duplicate values) // 2) sort the map entries, but don't forget to handle the duplicate value case (like i did) // 3) if you really need to return a map, use a LinkedHashMap which keep the insertion order 

The exec: http://www.ideone.com/dq3Lu

输出:

 Set entries: E -> -1 Set entries: B -> 1 Set entries: A -> 3 Set entries: C -> 3 Set entries: D -> 5 Set entries: H -> 15 Set entries: G -> 79 Set entries: F -> 1000 Added to result map entries: E -1 Added to result map entries: B 1 Added to result map entries: A 3 Added to result map entries: C 3 Added to result map entries: D 5 Added to result map entries: H 15 Added to result map entries: G 79 Added to result map entries: F 1000 Result map values: 5 Result map values: -1 Result map values: 1000 Result map values: 79 Result map values: 3 Result map values: 1 Result map values: 3 Result map values: 15 Result map entries: D -> 5 Result map entries: E -> -1 Result map entries: F -> 1000 Result map entries: G -> 79 Result map entries: A -> 3 Result map entries: B -> 1 Result map entries: C -> 3 Result map entries: H -> 15 

Hope it will help some folks

I've merged the solutions of user157196 and Carter Page:

 class MapUtil { public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue( Map<K, V> map ){ ValueComparator<K,V> bvc = new ValueComparator<K,V>(map); TreeMap<K,V> sorted_map = new TreeMap<K,V>(bvc); sorted_map.putAll(map); return sorted_map; } } class ValueComparator<K, V extends Comparable<? super V>> implements Comparator<K> { Map<K, V> base; public ValueComparator(Map<K, V> base) { this.base = base; } public int compare(K a, K b) { int result = (base.get(a).compareTo(base.get(b))); if (result == 0) result=1; // returning 0 would merge keys return result; } } 

If you have duplicate keys and only a small set of data (<1000) and your code is not performance critical you can just do the following:

 Map<String,Integer> tempMap=new HashMap<String,Integer>(inputUnsortedMap); LinkedHashMap<String,Integer> sortedOutputMap=new LinkedHashMap<String,Integer>(); for(int i=0;i<inputUnsortedMap.size();i++){ Map.Entry<String,Integer> maxEntry=null; Integer maxValue=-1; for(Map.Entry<String,Integer> entry:tempMap.entrySet()){ if(entry.getValue()>maxValue){ maxValue=entry.getValue(); maxEntry=entry; } } tempMap.remove(maxEntry.getKey()); sortedOutputMap.put(maxEntry.getKey(),maxEntry.getValue()); } 

inputUnsortedMap is the input to the code.

The variable sortedOutputMap will contain the data in decending order when iterated over. To change order just change > to a < in the if statement.

Is not the fastest sort but does the job without any additional dependencies.

You can try Guava's multimaps:

 TreeMap<Integer, Collection<String>> sortedMap = new TreeMap<>( Multimaps.invertFrom(Multimaps.forMap(originalMap), ArrayListMultimap.<Integer, String>create()).asMap()); 

As a result you get a map from original values to collections of keys that correspond to them. This approach can be used even if there are multiple keys for the same value.

Depending on the context, using java.util.LinkedHashMap<T> which rememebers the order in which items are placed into the map. Otherwise, if you need to sort values based on their natural ordering, I would recommend maintaining a separate List which can be sorted via Collections.sort() .