按值对Map<Key, Value>进行排序

我对Java比较陌生,经常发现我需要对值进行Map<Key, Value>排序。

由于值不是唯一的,我发现自己将keySet转换为array,并通过数组排序对该数组进行排序,并使用自定义比较器对与键关联的值进行排序。

有没有更简单的方法?

1672823 次浏览

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.util.LinkedHashMap<T>记住项目放置到映射中的顺序。否则,如果您需要根据它们的自然顺序对值进行排序,我建议维护一个可以通过Collections.sort()排序的单独列表。

共享集合库包含一个名为TreeBidiMap的解决方案。或者,您可以查看Google Colltions API。它有多树图您可以使用。

如果你不想使用这些框架……它们附带源代码。

如果你的Map值实现了比较(例如String),这应该可以

Map<Object, String> map = new HashMap<Object, String>();// Populate the MapList<String> mapValues = new ArrayList<String>(map.values());Collections.sort(mapValues);

如果map值本身没有实现比较,但您有一个可以对它们进行排序的比较实例,请将最后一行替换为:

Collections.sort(mapValues, comparable);

为了对键进行排序,我用TreeMap找到了一个更好的解决方案(我也会尝试为基于值的排序准备一个解决方案):

public static void main(String[] args) {Map<String, String> unsorted = new HashMap<String, String>();unsorted.put("Cde", "Cde_Value");unsorted.put("Abc", "Abc_Value");unsorted.put("Bcd", "Bcd_Value");
Comparator<String> comparer = new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {return o1.compareTo(o2);}};
Map<String, String> sorted = new TreeMap<String, String>(comparer);sorted.putAll(unsorted);System.out.println(sorted);}

产出将是:

{Abc=Abc_Value, Bcd=Bcd_Value, Cde=Cde_Value}

使用java.util.树映射

"映射根据其键的自然顺序排序,或者根据创建映射时提供的比较器排序,具体取决于使用的构造函数。"

对键进行排序需要比较器查找每个比较的每个值。一个更可扩展的解决方案将直接使用entrySet,从那时起,该值将立即可用于每次比较(尽管我没有用数字支持它)。

这是这样一个东西的通用版本:

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

对于上述解决方案,有一些方法可以减少内存轮换。例如,创建的第一个ArrayList可以重新用作返回值;这需要抑制一些泛型警告,但对于可重用的库代码来说可能是值得的。此外,不必在每次调用时重新分配比较器。

这是一个更有效但不那么吸引人的版本:

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

最后,如果您需要持续访问排序信息(而不仅仅是偶尔对其进行排序),您可以使用额外的多映射。如果您需要更多详细信息,请告诉我…

好的,这个版本适用于两个新的Map对象和两次迭代和值排序。希望,虽然map条目必须循环两次,但性能良好:

public static void main(String[] args) {Map<String, String> unsorted = new HashMap<String, String>();unsorted.put("Cde", "Cde_Value");unsorted.put("Abc", "Abc_Value");unsorted.put("Bcd", "Bcd_Value");
Comparator<String> comparer = new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {return o1.compareTo(o2);}};
System.out.println(sortByValue(unsorted, comparer));
}
public static <K, V> Map<K,V> sortByValue(Map<K, V> in, Comparator<? super V> compare) {Map<V, K> swapped = new TreeMap<V, K>(compare);for(Entry<K,V> entry: in.entrySet()) {if (entry.getValue() != null) {swapped.put(entry.getValue(), entry.getKey());}}LinkedHashMap<K, V> result = new LinkedHashMap<K, V>();for(Entry<V,K> entry: swapped.entrySet()) {if (entry.getValue() != null) {result.put(entry.getValue(), entry.getKey());}}return result;}

该解决方案使用带有比较器的TreeMap并对所有空键和值进行排序。首先,TreeMap的排序功能用于对值进行排序,接下来,排序后的Map用于创建结果作为保留具有相同值顺序的LinkedHashMap。

格哈德·格雷斯

当我遇到这个问题时,我只是在旁边创建了一个列表。如果你把它们放在一个自定义的Map实现中,它会有一种很好的感觉……你可以使用如下内容,仅在需要时执行排序。(注意:我还没有真正测试过这个,但它编译……可能是一个愚蠢的小bug)

(如果您希望按键和值对其进行排序,请让类扩展TreeMap,不要定义访问器方法,并让Mutators调用super.xxxxx而不是map_. xxxx)

package com.javadude.sample;
import java.util.ArrayList;import java.util.Collection;import java.util.Collections;import java.util.Comparator;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.Set;
public class SortedValueHashMap<K, V> implements Map<K, V> {private Map<K, V> map_ = new HashMap<K, V>();private List<V> valueList_ = new ArrayList<V>();private boolean needsSort_ = false;private Comparator<V> comparator_;
public SortedValueHashMap() {}public SortedValueHashMap(List<V> valueList) {valueList_ = valueList;}
public List<V> sortedValues() {if (needsSort_) {needsSort_ = false;Collections.sort(valueList_, comparator_);}return valueList_;}
// mutatorspublic void clear() {map_.clear();valueList_.clear();needsSort_ = false;}
public V put(K key, V value) {valueList_.add(value);needsSort_ = true;return map_.put(key, value);}
public void putAll(Map<? extends K, ? extends V> m) {map_.putAll(m);valueList_.addAll(m.values());needsSort_ = true;}
public V remove(Object key) {V value = map_.remove(key);valueList_.remove(value);return value;}
// accessorspublic boolean containsKey(Object key)           { return map_.containsKey(key); }public boolean containsValue(Object value)       { return map_.containsValue(value); }public Set<java.util.Map.Entry<K, V>> entrySet() { return map_.entrySet(); }public boolean equals(Object o)                  { return map_.equals(o); }public V get(Object key)                         { return map_.get(key); }public int hashCode()                            { return map_.hashCode(); }public boolean isEmpty()                         { return map_.isEmpty(); }public Set<K> keySet()                           { return map_.keySet(); }public int size()                                { return map_.size(); }public Collection<V> values()                    { return map_.values(); }}

虽然我同意不断需要对map进行排序可能是一种气味,但我认为以下代码是在不使用不同数据结构的情况下进行排序的最简单方法。

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

}

下面是一个不完整的单元测试:

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对象的排序列表,您可以从中获取键和值。

基于@devinmoore代码,一种使用泛型的地图排序方法,支持升序和降序。

/*** 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}

重要提示:

此代码可以以多种方式中断。如果您打算使用提供的代码,请务必阅读注释并了解其含义。例如,值不能再通过其键检索。(get总是返回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}

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

这里有一个我认为更合适的解决方案:

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

请注意,映射是从最高值到最低值进行排序的。

这是一个通用友好的版本:

public class MapUtil {public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {List<Entry<K, V>> list = new ArrayList<>(map.entrySet());list.sort(Entry.comparingByValue());
Map<K, V> result = new LinkedHashMap<>();for (Entry<K, V> entry : list) {result.put(entry.getKey(), entry.getValue());}
return result;}}

这太复杂了。地图不应该做按值排序这样的工作。最简单的方法是创建自己的类,使其符合您的要求。

在较低的示例中,您应该在*所在的位置添加TreeMap一个比较器。但是通过java API,它只给比较器提供键,而不是值。这里所述的所有示例都基于2个地图。一个哈希和一个新树。这很奇怪。

示例:

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

因此,将map更改为这样的集合:

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

您将创建类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;}}

比较器类:

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

通过这种方式,您可以轻松添加更多依赖项。

作为最后一点,我将添加简单的迭代器:

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

三个1行答案…

我会使用谷歌收藏<强>番石榴来做到这一点-如果你的值是Comparable,那么你可以使用

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

这将为map创建一个函数(对象)[将任何键作为输入,返回相应的值],然后对它们[值]应用自然(可比较)排序。

如果它们没有可比性,那么你需要按照以下方式做一些事情

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

这些可以应用于TreeMap(作为Ordering扩展Comparator)或一些排序后的LinkedHashMap

NB:如果你要使用TreeMap,请记住,如果比较==0,则该项目已经在列表中(如果你有多个比较相同的值,则会发生这种情况)。为了缓解这种情况,你可以像这样将你的键添加到比较器中(假设你的键和值是Comparable):

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

=将自然顺序应用于键映射的值,并将其与键的自然顺序复合

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

Ordering.onResultOf()Functions.for地图()

实施

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

map = ImmutableSortedMap.copyOf(myOriginalMap, valueComparator);

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

  1. 需要完成一个完整的完成地图
  2. 不要在TreeMap上尝试上面的比较器;当插入的键在put之后没有值时,尝试比较它是没有意义的,也就是说,它会很快崩溃

第1点对我来说有点破坏交易;谷歌集合非常懒惰(这很好:你可以在瞬间完成几乎所有操作;当你开始使用结果时,真正的工作就完成了),这需要复制一个整个地图!

“完整”答案/按值排序的实时地图

不过不要担心;如果你痴迷于以这种方式对“活”地图进行排序,你可以解决以上问题中的一个,而不是两个(!)

注意:这在2012年6月发生了重大变化-以前的代码永远无法工作:需要内部HashMap来查找值,而不会在TreeMap.get()->compare()compare()->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 loopsprivate 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 clobberedthis.valueMap = valueMap;}
public V put(K k, V v) {if (valueMap.containsKey(k)){//remove the key in the sorted set before adding the key againremove(k);}valueMap.put(k,v); //To get "real" unsorted values for the comparatorreturn 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 keysmap.put("e", 2);assertEquals(5, map.size());assertEquals(2, (int) map.get("e"));assertEquals(2, (int) map.get("d"));}}

当我们放置时,我们确保哈希映射具有比较器的值,然后将其放入TreeSet进行排序。但在此之前,我们检查哈希映射以查看键实际上不是重复的。此外,我们创建的比较器也将包含键,以便重复值不会删除不重复的键(由于==比较)。这2项是确保地图合约保留的第一项;如果你认为你不想这样,那么你几乎要完全反转地图(到Map<V,K>)。

构造函数需要被称为

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

这是一个面向对象的解决方案(即不使用static方法):

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

特此捐赠给公共领域。

这是Anthony答案的变体,如果有重复的值,则不起作用:

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

请注意,如何处理null还没有定论。

这种方法的一个重要优点是它实际上返回了一个Map,这与这里提供的其他一些解决方案不同。

public class SortedMapExample {
public static void main(String[] args) {Map<String, String> map = new HashMap<String, String>();
map.put("Cde", "C");map.put("Abc", "A");map.put("Cbc", "Z");map.put("Dbc", "D");map.put("Bcd", "B");map.put("sfd", "Bqw");map.put("DDD", "Bas");map.put("BGG", "Basd");
System.out.println(sort(map, new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {return o1.compareTo(o2);}}));}
@SuppressWarnings("unchecked")public static <K, V> Map<K,V> sort(Map<K, V> in, Comparator<? super V> compare) {Map<K, V> result = new LinkedHashMap<K, V>();V[] array = (V[])in.values().toArray();for(int i=0;i<array.length;i++){
}Arrays.sort(array, compare);for (V item : array) {K key= (K) getKey(in, item);result.put(key, item);}return result;}
public static <K, V>  Object getKey(Map<K, V> in,V value){Set<K> key= in.keySet();Iterator<K> keyIterator=key.iterator();while (keyIterator.hasNext()) {K valueObject = (K) keyIterator.next();if(in.get(valueObject).equals(value)){return valueObject;}}return null;}

}

//请在这里尝试。我正在修改值排序的代码。

Afaik最干净的方法是利用集合对map进行排序:

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 mapLinkedHashMap<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());}}

使用通用比较器,例如:

final class MapValueComparator<K,V extends Comparable<V>> implements Comparator<K> {private final 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));}}

当你有两个相等的项目时,投票最多的答案不起作用。TreeMap会留下相等的值。

例:未排序的地图

key/value: D/67.3key/value: A/99.5key/value: B/67.4key/value: C/67.5key/value: E/99.5

结果

key/value: A/99.5key/value: C/67.5key/value: B/67.4key/value: D/67.3

所以离开了E!!

对我来说,调整比较器工作得很好,如果它等于不返回0而是-1。

在示例中:

类值比较器实现比较器{

地图库;公有价值比较器{this.base=基数;}

比较对象a和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;}

}}

现在它返回:

未排序地图:

key/value: D/67.3key/value: A/99.5key/value: B/67.4key/value: C/67.5key/value: E/99.5

结果:

key/value: A/99.5key/value: E/99.5key/value: C/67.5key/value: B/67.4key/value: D/67.3

作为对外星人的回应(2011年11月22日):我正在使用这个解决方案来映射整数ID和名称,但想法是一样的,所以可能是上面的代码不正确(我会在测试中编写它并给你正确的代码),这是Map排序的代码,基于上面的解决方案:

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

}

这是测试类(我刚刚测试了它,这适用于整数,字符串映射:

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 {

@Testpublic 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);}}

以下是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 -1public 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;}}}

这是这个的测试用例:

@Testpublic 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);}

当然,你可以让这个更通用,但我只需要一个案例(地图)

这种方法只会达到目的。(“挫折”是值必须实现java.util.比较接口

  /**
* Sort a map according to values.
* @param <K> the key of the map.* @param <V> the value to sort according to.* @param mapToSort the map to sort.
* @return a map sorted on the values.
*/public static <K, V extends Comparable< ? super V>> Map<K, V>sortMapByValues(final Map <K, V> mapToSort){List<Map.Entry<K, V>> entries =new ArrayList<Map.Entry<K, V>>(mapToSort.size());
entries.addAll(mapToSort.entrySet());
Collections.sort(entries,new Comparator<Map.Entry<K, V>>(){@Overridepublic int compare(final Map.Entry<K, V> entry1,final Map.Entry<K, V> entry2){return entry1.getValue().compareTo(entry2.getValue());}});
Map<K, V> sortedMap = new LinkedHashMap<K, V>();
for (Map.Entry<K, V> entry : entries){sortedMap.put(entry.getKey(), entry.getValue());
}
return sortedMap;
}

由于TreeMap<>不起作用表示可以相等的值,我使用了这个:

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

您可能希望将列表放在链接哈希映射中,但如果您只想立即迭代它,那是多余的……

一些简单的更改,以便有一个排序的映射,其中包含具有重复值的对。在比较方法(类ValueCompator)中,当值相等时,不返回0,而是返回比较2个键的结果。键在映射中是不同的,因此您可以成功保留重复值(顺便说一下按键排序)。所以上面的例子可以像这样修改:

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

如果您有重复的键并且只有一小部分数据(<1000)并且您的代码不是性能关键,您可以执行以下操作:

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

输入未排序的数据是代码的输入。

变量已排序输出图将包含迭代时按降序排列的数据。要更改顺序,只需将if语句中的>更改为<。

不是最快的排序,但在没有任何其他依赖项的情况下完成工作。

当然,Stephen的解决方案真的很棒,但对于那些不会使用Guava的人来说:

这是我的按值排序映射的解决方案。此解决方案处理有两次相同值等的情况…

// If you want to sort a map by value, and if there can be twice the same value:
// here is your original mapMap<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 valueSet<Map.Entry<String,Integer>> set = new TreeSet<Map.Entry<String,Integer>>(new Comparator<Map.Entry<String,Integer>>(){@Overridepublic 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 hashmapMap<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

执行:http://www.ideone.com/dq3Lu

输出:

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

希望它能帮助一些人

而不是像有些人那样使用Collections.sort,我建议使用Arrays.sort。实际上,Collections.sort的作用是这样的:

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

它只是在列表中调用toArray,然后使用Arrays.sort。这样,所有的map条目将被复制三次:一次从map复制到临时列表(无论是LinkedList还是ArrayList),然后复制到临时数组,最后复制到新map。

我的解决方案省略了这一步,因为它不会创建不必要的LinkedList。这是代码,泛型友好和性能最佳:

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

我们只需像这样对地图进行排序

            Map<String, String> unsortedMap = new HashMap<String, String>();
unsortedMap.put("E", "E Val");unsortedMap.put("F", "F Val");unsortedMap.put("H", "H Val");unsortedMap.put("B", "B Val");unsortedMap.put("C", "C Val");unsortedMap.put("A", "A Val");unsortedMap.put("G", "G Val");unsortedMap.put("D", "D Val");
Map<String, String> sortedMap = new TreeMap<String, String>(unsortedMap);
System.out.println("\nAfter sorting..");for (Map.Entry <String, String> mapEntry : sortedMap.entrySet()) {System.out.println(mapEntry.getKey() + " \t" + mapEntry.getValue());

主要问题。如果您使用第一个答案(Google带您到这里),请更改比较器以添加相等子句,否则您无法通过键从sorted_map中获取值:

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}

创建自定义比较器并在创建新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;elsereturn ((Integer) map.get(o2)).compareTo((Integer)map.get(o1));
}}

在主函数中使用下面的代码

    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}

您可以尝试Guava的multimaps:

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

因此,您将获得从原始值到对应于它们的键集合的映射。即使同一个值有多个键,也可以使用这种方法。

因为map是无序的要对其进行排序,我们可以执行以下操作

Map<String, String> map= new TreeMap<String, String>(unsortMap);

您应该注意,与哈希映射不同,树映射保证其元素将按升序键顺序排序。

要实现这一点,Java8中的新功能:

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

使用给定的比较器按它们的值排序。或者,如果您的值是相互可比的,则不需要显式比较器:

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

返回的可迭代对象在每次迭代时都会创建给定映射的新快照,因此除非并发修改,否则它将始终反映映射的当前状态。

使用Java8,您可以使用流api以一种明显不那么冗长的方式完成它:

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

我合并了user157196和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 keysreturn result;}}

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

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

这将允许您使用按值升序排序的条目。如果您想要降序值,只需反转比较器:

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

然后,你可以继续使用其他流操作来消耗数据。例如,如果你想要新地图中的前10名:

Map<K,V> topTen =map.entrySet().stream().sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).limit(10).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));

上面看到的#0按照插入的顺序迭代条目。

或者打印到System.out

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

最佳方法

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

如果没有任何值大于map的大小,你可以使用数组,这应该是最快的方法:

public List<String> getList(Map<String, Integer> myMap) {String[] copyArray = new String[myMap.size()];for (Entry<String, Integer> entry : myMap.entrySet()) {copyArray[entry.getValue()] = entry.getKey();}return Arrays.asList(copyArray);}
    static <K extends Comparable<? super K>, V extends Comparable<? super V>>Map sortByValueInDescendingOrder(final Map<K, V> map) {Map re = new TreeMap(new Comparator<K>() {@Overridepublic int compare(K o1, K o2) {if (map.get(o1) == null || map.get(o2) == null) {return -o1.compareTo(o2);}int result = -map.get(o1).compareTo(map.get(o2));if (result != 0) {return result;}return -o1.compareTo(o2);}});re.putAll(map);return re;}@Test(timeout = 3000l, expected = Test.None.class)public void testSortByValueInDescendingOrder() {char[] arr = "googler".toCharArray();Map<Character, Integer> charToTimes = new HashMap();for (int i = 0; i < arr.length; i++) {Integer times = charToTimes.get(arr[i]);charToTimes.put(arr[i], times == null ? 1 : times + 1);}Map sortedByTimes = sortByValueInDescendingOrder(charToTimes);Assert.assertEquals(charToTimes.toString(), "{g=2, e=1, r=1, o=2, l=1}");Assert.assertEquals(sortedByTimes.toString(), "{o=2, g=2, r=1, l=1, e=1}");Assert.assertEquals(sortedByTimes.containsKey('a'), false);Assert.assertEquals(sortedByTimes.get('a'), null);Assert.assertEquals(sortedByTimes.get('g'), 2);Assert.assertEquals(sortedByTimes.equals(charToTimes), true);}

这个问题已经有很多答案了,但没有一个提供给我我想要的东西,一个映射实现,它返回按关联值排序的键和条目,并在映射中修改键和值时维护此属性。两个其他问题特别要求这样做。

我想出了一个通用的友好示例来解决这个用例。这个实现并不尊重Map接口的所有契约,例如在原始对象中从keySet()和entrySet()返回的集合中反映值更改和删除。我觉得这样的解决方案太大,无法包含在Stack Overflow答案中。如果我设法创建一个更完整的实现,也许我会把它发布到Github,然后在这个答案的更新版本中链接到它。

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

我的解决方案是一种非常简单的方法,主要使用给定的API。我们使用地图的特性通过参数名entrySet()方法将其内容导出为设置。我们现在有一个包含地图入口对象的设置

好的,Set没有顺序,但我们可以将内容放入数组列表中。它现在有一个随机顺序,但我们无论如何都会对其进行排序。

由于数组列表收藏,我们现在使用Collections.sort()方法为混乱带来秩序。因为我们的地图入口对象没有实现我们需要的那种比较,我们提供了一个自定义比较器

public static void main(String[] args) {HashMap<String, String> map = new HashMap<>();map.put("Z", "E");map.put("G", "A");map.put("D", "C");map.put("E", null);map.put("O", "C");map.put("L", "D");map.put("Q", "B");map.put("A", "F");map.put(null, "X");MapEntryComparator mapEntryComparator = new MapEntryComparator();
List<Entry<String,String>> entryList = new ArrayList<>(map.entrySet());Collections.sort(entryList, mapEntryComparator);
for (Entry<String, String> entry : entryList) {System.out.println(entry.getKey() + " : " + entry.getValue());}
}

下面是Java8的代码算术软件

Map<String, Integer> map = N.asMap("a", 2, "b", 3, "c", 1, "d", 2);Map<String, Integer> sortedMap = Stream.of(map.entrySet()).sorted(Map.Entry.comparingByValue()).toMap(e -> e.getKey(), e -> e.getValue(),LinkedHashMap::new);N.println(sortedMap);// output: {c=1, a=2, d=2, b=3}

声明:我是AbacusUtil的开发者。

最好的办法是将HashMap转换为TreeMap。TreeMap自己对键进行排序。如果你想对值进行排序,而不是快速修复,如果你的值不重复,你可以用键切换值。

public class Test {public static void main(String[] args) {TreeMap<Integer, String> hm=new TreeMap();hm.put(3, "arun singh");hm.put(5, "vinay singh");hm.put(1, "bandagi singh");hm.put(6, "vikram singh");hm.put(2, "panipat singh");hm.put(28, "jakarta singh");
ArrayList<String> al=new ArrayList(hm.values());Collections.sort(al, new myComparator());
System.out.println("//sort by values \n");for(String obj: al){for(Map.Entry<Integer, String> map2:hm.entrySet()){if(map2.getValue().equals(obj)){System.out.println(map2.getKey()+" "+map2.getValue());}}}}}
class myComparator implements Comparator{@Overridepublic int compare(Object o1, Object o2) {String o3=(String) o1;String o4 =(String) o2;return o3.compareTo(o4);}}

输出=

//sort by values
3 arun singh1 bandagi singh28 jakarta singh2 panipat singh6 vikram singh5 vinay singh

迟到的入口。

随着Java-8的出现,我们可以以一种非常简单/简洁的方式使用流进行数据操作。您可以使用流按值对映射条目进行排序,并创建一个保留插入顺序迭代的LinkedHashMap

例如:

LinkedHashMap sortedByValueMap = map.entrySet().stream().sorted(comparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey))     //first sorting by Value, then sorting by Key(entries with same value).collect(LinkedHashMap::new,(map,entry) -> map.put(entry.getKey(),entry.getValue()),LinkedHashMap::putAll);

对于反向排序,替换:

comparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey)

comparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey).reversed()

如果首选具有Map数据结构,该数据结构固有地按值排序,而无需触发任何排序方法或显式传递给实用程序,那么以下解决方案可能适用:

(1)org.drools.chance.core.util.ValueSortedMap(JBoss项目)在内部维护两个map,一个用于查找,一个用于维护排序值。与之前添加的答案非常相似,但可能是抽象和封装部分(包括复制机制)使其从外部使用更安全。

(2)http://techblog.molindo.at/2008/11/java-map-sorted-by-value.html避免维护两个map,而是依赖/扩展Apache Common的LinkedMap。

// required to access LinkEntry.before and LinkEntry.afterpackage org.apache.commons.collections.map;
// SNIP: imports
/*** map implementation based on LinkedMap that maintains a sorted list of* values for iteration*/public class ValueSortedHashMap extends LinkedMap {private final boolean _asc;
// don't use super()!public ValueSortedHashMap(final boolean asc) {super(DEFAULT_CAPACITY);_asc = asc;}
// SNIP: some more constructors with initial capacity and the like
protected void addEntry(final HashEntry entry, final int hashIndex) {final LinkEntry link = (LinkEntry) entry;insertSorted(link);data[hashIndex] = entry;}
protected void updateEntry(final HashEntry entry, final Object newValue) {entry.setValue(newValue);final LinkEntry link = (LinkEntry) entry;link.before.after = link.after;link.after.before = link.before;link.after = link.before = null;insertSorted(link);}
private void insertSorted(final LinkEntry link) {LinkEntry cur = header;// iterate whole list, could (should?) be replaced with quicksearch// start at end to optimize speed for in-order insertionswhile ((cur = cur.before) != header & amp; & amp; !insertAfter(cur, link)) {}link.after = cur.after;link.before = cur;cur.after.before = link;cur.after = link;}
protected boolean insertAfter(final LinkEntry cur, final LinkEntry link) {if (_asc) {return ((Comparable) cur.getValue()).compareTo((V) link.getValue()) & lt; = 0;} else {return ((Comparable) cur.getValue()).compareTo((V) link.getValue()) & gt; = 0;}}
public boolean isAscending() {return _asc;}}

(3)编写一个自定义的Map或从LinkedHashMap扩展,仅根据需要在枚举期间(例如,values()keyset()entryset())进行排序。内部实现/行为是从使用此类的实现/行为中抽象出来的,但在此类的客户端看来,当请求枚举时,值总是被排序的。此类希望如果所有put操作在枚举之前都完成了,排序将主要发生一次。排序方法采用了之前对这个问题的一些回答。

public class SortByValueMap<K, V> implements Map<K, V> {
private boolean isSortingNeeded = false;
private final Map<K, V> map = new LinkedHashMap<>();
@Overridepublic V put(K key, V value) {isSortingNeeded = true;return map.put(key, value);}
@Overridepublic void putAll(Map<? extends K, ? extends V> map) {isSortingNeeded = true;map.putAll(map);}
@Overridepublic Set<K> keySet() {sort();return map.keySet();}
@Overridepublic Set<Entry<K, V>> entrySet() {sort();return map.entrySet();}
@Overridepublic Collection<V> values() {sort();return map.values();}
private void sort() {if (!isSortingNeeded) {return;}
List<Entry<K, V>> list = new ArrayList<>(size());
for (Iterator<Map.Entry<K, V>> it = map.entrySet().iterator(); it.hasNext();) {Map.Entry<K, V> entry = it.next();list.add(entry);it.remove();}
Collections.sort(list);
for (Entry<K, V> entry : list) {map.put(entry.getKey(), entry.getValue());}
isSortingNeeded = false;}
@Overridepublic String toString() {sort();return map.toString();}}

(4)Guava提供了ImmutableMap.Builder.orderEntriesByValue(比较器值),尽管生成的map是不可变的:

将此Builder配置为根据值按条目排序指定的比较器。

排序顺序是稳定的,也就是说,如果两个条目的值作为等效比较,首先插入的条目将是第一个在构建的映射的迭代顺序中。

我重写了devinmoore的方法,该方法在不使用Iterator的情况下按其值对map进行排序:

public static Map<K, V> sortMapByValue(Map<K, V> inputMap) {
Set<Entry<K, V>> set = inputMap.entrySet();List<Entry<K, V>> list = new ArrayList<Entry<K, V>>(set);
Collections.sort(list, new Comparator<Map.Entry<K, V>>(){@Overridepublic int compare(Entry<K, V> o1, Entry<K, V> o2) {return (o1.getValue()).compareTo( o2.getValue() );  //Ascending order}} );
Map<K, V> sortedMap = new LinkedHashMap<>();
for(Map.Entry<K, V> entry : list){sortedMap.put(entry.getKey(), entry.getValue());}
return sortedMap;}

Note:,我们使用LinkedHashMap作为输出映射,因为我们的列表已按值排序,现在我们应该将列表存储到输出映射中,并按插入键、值的顺序排列。因此,如果您使用例如TreeMap作为输出映射,您的映射将再次按映射键排序!

这是主要的方法:

public static void main(String[] args) {Map<String, String> map = new HashMap<>();map.put("3", "three");map.put("1", "one");map.put("5", "five");System.out.println("Input Map:" + map);System.out.println("Sorted Map:" + sortMapByValue(map));}

最后,这是输出:

Input Map:{1=one, 3=three, 5=five}Sorted Map:{5=five, 1=one, 3=three}

最简单蛮力sortHashMap方法HashMap<String, Long>:你可以像这样复制粘贴并使用

public class Test  {public static void main(String[] args)  {HashMap<String, Long> hashMap = new HashMap<>();hashMap.put("Cat", (long) 4);hashMap.put("Human", (long) 2);hashMap.put("Dog", (long) 4);hashMap.put("Fish", (long) 0);hashMap.put("Tree", (long) 1);hashMap.put("Three-legged-human", (long) 3);hashMap.put("Monkey", (long) 2);
System.out.println(hashMap);  //{Human=2, Cat=4, Three-legged-human=3, Monkey=2, Fish=0, Tree=1, Dog=4}System.out.println(sortHashMap(hashMap));  //{Cat=4, Dog=4, Three-legged-human=3, Human=2, Monkey=2, Tree=1, Fish=0}}
public LinkedHashMap<String, Long> sortHashMap(HashMap<String, Long> unsortedMap)  {LinkedHashMap<String, Long> result = new LinkedHashMap<>();
//add String keys to an array: the array would get sorted, based on those keys' valuesArrayList<String> sortedKeys = new ArrayList<>();for (String key: unsortedMap.keySet())  {sortedKeys.add(key);}
//sort the ArrayList<String> of keysfor (int i=0; i<unsortedMap.size(); i++)  {for (int j=1; j<sortedKeys.size(); j++)  {if (unsortedMap.get(sortedKeys.get(j)) > unsortedMap.get(sortedKeys.get(j-1))) {String temp = sortedKeys.get(j);sortedKeys.set(j, sortedKeys.get(j-1));sortedKeys.set(j-1, temp);}}}
// construct the result Mapfor (String key: sortedKeys)  {result.put(key, unsortedMap.get(key));}
return result;}}

发布我的回答版本

List<Map.Entry<String, Integer>> list = new ArrayList<>(map.entrySet());Collections.sort(list, (obj1, obj2) -> obj2.getValue().compareTo(obj1.getValue()));Map<String, Integer> resultMap = new LinkedHashMap<>();list.forEach(arg0 -> {resultMap.put(arg0.getKey(), arg0.getValue());});System.out.println(resultMap);

使用Guava库:

public static <K,V extends Comparable<V>>SortedMap<K,V> sortByValue(Map<K,V> original){var comparator = Ordering.natural().reverse() // highest first.nullsLast().onResultOf(Functions.forMap(original, null)).compound(Ordering.usingToString());return ImmutableSortedMap.copyOf(original, comparator);}

为每个值创建一个条目列表,其中值被排序
要求Java8或以上

Map<Double,List<Entry<String,Double>>> sorted =map.entrySet().stream().collect( Collectors.groupingBy( Entry::getValue, TreeMap::new,Collectors.mapping( Function.identity(), Collectors.toList() ) ) );

使用映射{[A=99.5],[B=67.4],[C=67.4],[D=67.3]}
获取{67.3=[D=67.3], 67.4=[B=67.4, C=67.4], 99.5=[A=99.5]}


…以及如何一个接一个地访问每个条目:

sorted.entrySet().forEach( e -> e.getValue().forEach( l -> System.out.println( l ) ) );

D=67.3B=67.4C=67.4A=99.5

给定地图

   Map<String, Integer> wordCounts = new HashMap<>();wordCounts.put("USA", 100);wordCounts.put("jobs", 200);wordCounts.put("software", 50);wordCounts.put("technology", 70);wordCounts.put("opportunity", 200);

根据值按升序对地图进行排序

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

根据值按降序对地图进行排序

Map<String,Integer>  sortedMapReverseOrder =  wordCounts.entrySet().stream().sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));System.out.println(sortedMapReverseOrder);

输出:

{软件=50,技术=70,美国=100,工作=200,机会=200}

{工作=200,机会=200,美国=100,技术=70,软件=50}

使用LinkedList

//Create a list by HashMapList<Map.Entry<String, Double>> list = new LinkedList<>(hashMap.entrySet());
//Sorting the listCollections.sort(list, new Comparator<Map.Entry<String, Double>>() {public int compare(Map.Entry<String, Double> o1, Map.Entry<String, Double> o2) {return (o1.getValue()).compareTo(o2.getValue());}});
//put data from sorted list to hashmapHashMap<String, Double> sortedData = new LinkedHashMap<>();for (Map.Entry<String, Double> data : list) {sortedData.put(data.getKey(), data.getValue());}
System.out.print(sortedData);

这具有能够排序升序或降序的额外好处,使用Java8

import static java.util.Comparator.comparingInt;import static java.util.stream.Collectors.toMap;import java.util.LinkedHashMap;import java.util.Map;import java.util.Map.Entry;import java.util.stream.Collectors;import java.util.stream.Stream;
class Utils {public static Map<String, Integer> sortMapBasedOnValues(Map<String, Integer> map, boolean descending) {int multiplyBy = (descending) ? -1: 1;Map<String, Integer> sorted =  map.entrySet().stream().sorted(comparingInt(e -> multiplyBy * e.getValue() )).collect(toMap(Map.Entry::getKey,Map.Entry::getValue,(a, b) -> { throw new AssertionError();},LinkedHashMap::new));return sorted;}}

对Java8及以上的任何地图进行排序的简单方法

Map<String, Object> mapToSort = new HashMap<>();
List<Map.Entry<String, Object>> list = new LinkedList<>(mapToSort.entrySet());
Collections.sort(list, Comparator.comparing(o -> o.getValue().getAttribute()));
HashMap<String, Object> sortedMap = new LinkedHashMap<>();for (Map.Entry<String, Object> map : list) {sortedMap.put(map.getKey(), map.getValue());}

如果您使用Java7及以下

Map<String, Object> mapToSort = new HashMap<>();
List<Map.Entry<String, Object>> list = new LinkedList<>(mapToSort.entrySet());
Collections.sort(list, new Comparator<Map.Entry<String, Object>>() {@Overridepublic int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) {return o1.getValue().getAttribute().compareTo(o2.getValue().getAttribute());}});
HashMap<String, Object> sortedMap = new LinkedHashMap<>();for (Map.Entry<String, Object> map : list) {sortedMap.put(map.getKey(), map.getValue());}
map = your hashmap;
List<Map.Entry<String, Integer>> list = new LinkedList<Map.Entry<String, Integer>>(map.entrySet());Collections.sort(list, new cm());//IMP
HashMap<String, Integer> sorted = new LinkedHashMap<String, Integer>();for(Map.Entry<String, Integer> en: list){sorted.put(en.getKey(),en.getValue());}
System.out.println(sorted);//sorted hashmap

创建新的类

class cm implements Comparator<Map.Entry<String, Integer>>{@Overridepublic int compare(Map.Entry<String, Integer> a,Map.Entry<String, Integer> b){return (a.getValue()).compareTo(b.getValue());}}

我可以给你一个例子,但肯定这是你需要的。

map = {10 = 3, 11 = 1,12 = 2}

假设您想要前2个最频繁的键,即(10,12)因此,最简单的方法是使用PriorityQueue根据映射的值进行排序。

PriorityQueue<Integer> pq = new PriorityQueue<>((a, b) -> (map.get(a) - map.get(b));for(int key: map.keySets()) {pq.add(key);if(pq.size() > 2) {pq.poll();}}// Now pq has the top 2 most frequent key based on value. It sorts the value.

这可以通过Java 8轻松实现

public static LinkedHashMap<Integer, String> sortByValue(HashMap<Integer, String> map) {
List<Map.Entry<Integer, String>> list = new ArrayList<>(map.entrySet());list.sort(Map.Entry.comparingByValue());LinkedHashMap<Integer, String> sortedMap = new LinkedHashMap<>();list.forEach(e -> sortedMap.put(e.getKey(), e.getValue()));return sortedMap;}

排序任何Hashmap最简单的方法在Java。我们不需要将其存储在树形图,列表等中。

在这里,我将使用JavaStreams:

让我们按其值对此地图进行排序(升序)

Map<String, Integer> mp= new HashMap<>();mp.put("zebra", 1);mp.put("blossom", 2);mp.put("gemini", 3);mp.put("opera", 7);mp.put("adelaide", 10);
Map<String, Integer> resultMap= mp.entrySet().stream().sorted(Map.Entry.<String, Integer>comparingByValue()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,(e1, e2) -> e1, LinkedHashMap::new));

您现在可以通过多种方式打印排序的结果映射,例如使用高级for循环或迭代器。

上面的map也可以按值的降序排序

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

现在让我们采取另一个场景,我们将“User”存储在map中,并根据“User”的“name”按升序(字典顺序)对其进行排序:

User u1= new User("hi", 135);User u2= new User("bismuth", 900);User u3= new User("alloy", 675);User u4= new User("jupiter", 342);User u5= new User("lily", 941);
Map<String, User> map2= new HashMap<>();map2.put("zebra", u3);map2.put("blossom", u5);map2.put("gemini", u1);map2.put("opera", u2);map2.put("adelaide", u4);

Map<String, User>  resultMap=map2.entrySet().stream().sorted(Map.Entry.<String, User>comparingByValue( (User o1, User o2)-> o1.getName().compareTo(o2.getName()))).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,(e1, e2) -> e2, LinkedHashMap::new));


class User{String name;int id;        

public User(String name, int id) {super();this.name = name;this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getId() {return id;}public void setId(int id) {this.id = id;}@Overridepublic String toString() {return "User [name=" + name + ", id=" + id + "]";}@Overridepublic int hashCode() {final int prime = 31;int result = 1;result = prime * result + id;result = prime * result + ((name == null) ? 0 : name.hashCode());return result;}@Overridepublic boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (getClass() != obj.getClass())return false;User other = (User) obj;if (id != other.id)return false;if (name == null) {if (other.name != null)return false;} else if (!name.equals(other.name))return false;return true;

}}

在TreeMap中,键按自然顺序排序。例如,如果您对数字进行排序,(请注意4的顺序)

{0=0, 10=10, 20=20, 30=30, 4=4, 50=50, 60=60, 70=70}

为了解决这个问题,在Java8中,首先检查字符串长度,然后比较。

Map<String, String> sortedMap = new TreeMap<>Comparator.comparingInt(String::length).thenComparing(Function.identity()));

{0=0, 4=4, 10=10, 20=20, 30=30, 50=50, 60=60, 70=70}

    Map<String, Integer> map = new HashMap<>();map.put("b", 2);map.put("a", 1);map.put("d", 4);map.put("c", 3);    
// ----- Using Java 7 -------------------List<Map.Entry<String, Integer>> entries = new ArrayList<>(map.entrySet());Collections.sort(entries, (o1, o2) -> o1.getValue().compareTo(o2.getValue()));System.out.println(entries); // [a=1, b=2, c=3, d=4]

// ----- Using Java 8 Stream API --------map.entrySet().stream().sorted(Map.Entry.comparingByValue()).forEach(System.out::println); // {a=1, b=2, c=3, d=4}
    

Geeks对于Geeks按值排序HashMap

Input : Key = Math, Value = 98Key = Data Structure, Value = 85Key = Database, Value = 91Key = Java, Value = 95Key = Operating System, Value = 79Key = Networking, Value = 80
Output : Key = Operating System, Value = 79Key = Networking, Value = 80Key = Data Structure, Value = 85Key = Database, Value = 91Key = Java, Value = 95Key = Math, Value = 98Solution: The idea is to store the entry set in a list and sort the list on the basis of values. Then fetch values and keys from the list and put them in a new hashmap. Thus, a new hashmap is sorted according to values.Below is the implementation of the above idea:



// Java program to sort hashmap by valuesimport java.util.*;import java.lang.*; 
public class GFG { 
// function to sort hashmap by valuespublic static HashMap<String, Integer> sortByValue(HashMap<String, Integer> hm){// Create a list from elements of HashMapList<Map.Entry<String, Integer> > list =new LinkedList<Map.Entry<String, Integer> >(hm.entrySet()); 
// Sort the listCollections.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());}});         
// put data from sorted list to hashmapHashMap<String, Integer> temp = new LinkedHashMap<String, Integer>();for (Map.Entry<String, Integer> aa : list) {temp.put(aa.getKey(), aa.getValue());}return temp;} 
// Driver Codepublic static void main(String[] args){ 
HashMap<String, Integer> hm = new HashMap<String, Integer>(); 
// enter data into hashmaphm.put("Math", 98);hm.put("Data Structure", 85);hm.put("Database", 91);hm.put("Java", 95);hm.put("Operating System", 79);hm.put("Networking", 80);Map<String, Integer> hm1 = sortByValue(hm); 
// print the sorted hashmapfor (Map.Entry<String, Integer> en : hm1.entrySet()) {System.out.println("Key = " + en.getKey() +", Value = " + en.getValue());}}}OutputKey = Operating System, Value = 79Key = Networking, Value = 80Key = Data Structure, Value = 85Key = Database, Value = 91Key = Java, Value = 95Key = Math, Value = 98