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

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

有没有更简单的方法?


当前回答

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

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

其他回答

为每个值创建一个条目列表,其中对值进行排序需要Java 8或更高版本

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.3],[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.3 B=67.4 C=67.4 A=99.5

如果您的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);

这太复杂了。地图不应该按价值排序。最简单的方法是创建自己的类,以满足您的需求。

在下面的示例中,您应该在*所在的位置添加TreeMap比较器。但通过javaAPI,它只提供比较器键,而不提供值。此处所述的所有示例均基于2个地图。一个哈希和一个新树。这很奇怪。

示例:

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

因此,通过以下方式将地图更改为集合:

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

以及Comparator类:

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

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