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

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

有没有更简单的方法?


当前回答

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

注意,如何处理空值还很难说。

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

其他回答

创建自定义比较器,并在创建新的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));

    }
}

在主函数中使用以下代码

    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}

如果您的Map值实现Comparable(例如String),那么这应该会起作用

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

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

Collections.sort(mapValues, comparable);

我重写了devinmore的方法,该方法在不使用迭代器的情况下,根据地图的值对其进行排序:

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>>()
    {
        @Override
        public 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;
}

注意:我们使用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}

commons集合库包含一个名为TreeBidiMap的解决方案。或者,你可以看看谷歌收藏API。它有你可以使用的TreeMultimap。

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

使用通用比较器,例如:

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