基于值将Map<Key,Value>按降序排序

36

可能是重复问题:
如何在Java中按值对Map键值对进行排序?

我正在使用map接口从文件中读取并将其存储为键值对。文件格式如下:

 A 34
 B 25
 c 50

我将从这个文件读取数据,并将其存储为键值对,然后将其显示给用户。我的要求是按照以下格式显示结果:

C 50
A 34
B 25

因此,我需要将地图按值的降序排序。这样我就能将它们显示为我的结果...我已经阅读了相关内容,并找到了以下代码:

static <K,V extends Comparable<? super V>> SortedSet<Map.Entry<K,V>> entriesSortedByValues(Map<K,V> map) {
        SortedSet<Map.Entry<K,V>> sortedEntries = new TreeSet<Map.Entry<K,V>>(
            new Comparator<Map.Entry<K,V>>() {
                @Override public int compare(Map.Entry<K,V> e1, Map.Entry<K,V> e2) {
                    int res = e1.getValue().compareTo(e2.getValue());
                    return res != 0 ? res : 1; // Special fix to preserve items with equal values
                }
            }
        );
        sortedEntries.addAll(map.entrySet());
        return sortedEntries;
    }

我希望这将按升序排序值,我只想知道这种方法是否正确,或者其他有效的方法对我有帮助吗?


也许你应该看一下Guava。请参考这个评论。 - Andrew Logvinov
@NandaKumar:Set不允许重复,例如使用List。如果开始允许它们,您将违反Comparator的契约! - dacwe
map.entrySet().stream().sorted(Comparator.comparing(e->-e.getValue())).forEach(System.out::println); - manoj jangam
2个回答

55

由于您可能会有重复的值,因此根本不应该使用Set。改用List并对其进行排序。您的entriesSortedByValues应该像这样:

static <K,V extends Comparable<? super V>> 
            List<Entry<K, V>> entriesSortedByValues(Map<K,V> map) {

    List<Entry<K,V>> sortedEntries = new ArrayList<Entry<K,V>>(map.entrySet());

    Collections.sort(sortedEntries, 
            new Comparator<Entry<K,V>>() {
                @Override
                public int compare(Entry<K,V> e1, Entry<K,V> e2) {
                    return e2.getValue().compareTo(e1.getValue());
                }
            }
    );

    return sortedEntries;
}

注意:在您的示例输出中,值是降序的。如果您想要升序,请使用e1.getValue().compareTo(e2.getValue())


示例:

public static void main(String args[]) {

    Map<String, Integer> map = new HashMap<String, Integer>();
    map.put("A", 34);
    map.put("B", 25);
    map.put("C", 50);
    map.put("D", 50); // "duplicate" value

    System.out.println(entriesSortedByValues(map));
}

输出:

[D=50, C=50, A=34, B=25]

可以用。谢谢! - Even Cheng
每个人在查看此内容时应该使用 sortedEntries.sort() 而不是 Collections,像这样:sortedEntries.sort((e1, e2) -> e2.getValue().compareTo(e1.getValue())); - SocketByte

14

编写自己的比较器(comparator)并将其传递给TreeMap

class MyComparator implements Comparator {

Map map;

public MyComparator(Map map) {
    this.map = map;
}

public int compare(Object o1, Object o2) {

    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", 25);
    lMap.put("C", 50);

    MyComparator comp=new MyComparator(lMap);

    Map<String,Integer> newMap = new TreeMap(comp);
    newMap.putAll(lMap);

输出:

C=50
A=35
B=25

2
如果您添加 lMap.put("D", 50),则此代码将无法正常工作,因为它会被视为重复项(实际上会覆盖任何其他值,例如 "C")。 - dacwe
1
不允许重复的值。 - amicngh
1
@amicngh:非常感谢您提供的解决方案。但是,我希望允许重复值。有可能实现吗?谢谢。 - NandaKumar
以下代码可以正常运行。public Map sortByValue(Map map) { List list = new LinkedList(map.entrySet()); Collections.sort(list, new Comparator() { public int compare(Object o2, Object o1) { return ((Comparable) ((Map.Entry) (o1)).getValue()) .compareTo(((Map.Entry) (o2)).getValue()); } }); Map result = new LinkedHashMap(); for (Iterator it = list.iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry)it.next(); result.put(entry.getKey(), entry.getValue()); } return result; } - Learner
1
你应该使用new TreeMap(Collections.reverseOrder());。Map<String, Integer> newMap = new TreeMap(Collections.reverseOrder()); newMap.putAll(myMap); - Tell Me How
@NandaKumar 您可以在 compareTo 方法中添加 if 子句,以确定如果两个值相同时要执行什么操作。例如,在此处,如果两个值相同,则按键的降序排序。 public int compare(Object o1, Object o2) { if(map.get(o2) == map.get(o1)) return o2.toString().compareTo(o1.toString()); return ((Integer) map.get(o2)).compareTo((Integer) map.get(o1)); - acacia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接