Java Map 按值排序

Posted

技术标签:

【中文标题】Java Map 按值排序【英文标题】:Java Map sort by value 【发布时间】:2012-11-30 22:08:25 【问题描述】:

我正在寻找按值对Map<String, Integer> 进行排序的方法。我找到了this post,它解决了我的排序问题,但不完全是。根据帖子,我写了以下代码:

import java.util.*;

public class Sort 

    static class ValueComparator implements Comparator<String> 

        Map<String, Integer> base;

        ValueComparator(Map<String, Integer> base) 
            this.base = base;
        

        @Override
        public int compare(String a, String b) 
            if (base.get(a) >= base.get(b)) 
                return 1;
             else 
                return -1;
            
        
    

    public static void main(String[] args) 
        HashMap<String, Integer> map = new HashMap<String, Integer>();
        ValueComparator vc = new ValueComparator(map);
        TreeMap<String, Integer> sorted = new TreeMap<String, Integer>(vc);
        map.put("A", 1);
        map.put("B", 2);
        sorted.putAll(map);
        for (String key : sorted.keySet()) 
            System.out.println(key + " : " + sorted.get(key)); // why null values here?
        
        System.out.println(sorted.values()); // But we do have non-null values here!
    

输出:

A : null
B : null
[1, 2]
BUILD SUCCESSFUL (total time: 0 seconds)

从输出中可以看出,get 方法总是返回 null。原因是我的ValueComparator.compare() 方法永远不会返回0,这是我通过制作this post 发现的。

有人在那篇帖子中建议以下解决null值问题:

        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 0;
            
            return -1;  
        

我已经测试了这段代码,它引入了一个关键的合并问题。换句话说,当值相等时,它们对应的键被合并。

我还尝试了以下方法:

            public int compare(String a, String b) 
                if (a.equals(b)) return 0;
                if (base.get(a) >= base.get(b)) 
                    return 1;
                 else return -1;
            

它也不起作用。一些值仍然是null。此外,这种解决方法可能存在逻辑问题。

任何人都可以为我的问题提出一个完全可行的解决方案吗?我希望按值排序函数和get 方法同时工作。

【问题讨论】:

为什么/如何按值对地图进行排序?我认为这甚至没有意义……这不就是 Lists 和 Sets 的用途吗?只需调用 Map.values() 并将结果添加到 TreeSet 或有序 List @jahroy 您如何将排序后的值与其对应的键关联起来?我不想手动维护键和值之间的关系。 OK... 所以您希望根据值的顺序对 进行排序。我我明白你现在想要什么了。 在这种情况下,你应该使用这个:return base.get(a).compareTo(base.get(b)) @jahroy 如果您有比使用地图更好的解决方案,那也很好:) 【参考方案1】:

在您的比较函数中,当值相等时,您应该比较键。这将确保具有相同值的不同键不会被“合并”,因为它消除了原本会比较相等的条目的歧义。

例如:

    @Override
    public int compare(String a, String b) 
        Integer x = base.get(a);
        Integer y = base.get(b);
        if (x.equals(y)) 
            return a.compareTo(b);
        
        return x.compareTo(y);
    

(您需要修改上面的代码以匹配您的空值策略)

请注意,您对值进行排序的方法非常脆弱。您的“排序”地图将不支持添加新条目,这可能会令人困惑。

【讨论】:

谢谢!解决了我的问题。【参考方案2】:

试试这个:

return base.get(a).compareTo(base.get(b));

为了证明你对自动拆箱的错误:

Integer a = new Integer(2);
Integer b = new Integer(2);
boolean isEqual = ( a == b );
System.out.println("equal: " + isEqual);
System.out.println("a: " + a);
System.out.println("b: " + b);

我的输出是:

equal: false
a: 2
b: 2

【讨论】:

它有相同的键合并问题。对了,你可以直接使用==比较Integer对象。 好点!我不知道。但即使使用正确的比较方法,我的问题仍然存在:(【参考方案3】:

试试这个...

HashMap<String, Integer> h = new HashMap<String, Integer>();
h.put("z",30);
h.put("e",10);
h.put("b",20);
h.put("c",20);
List<Map.Entry> a = new ArrayList<Map.Entry>(h.entrySet());
Collections.sort(a,
         new Comparator() 
             public int compare(Object o1, Object o2) 
                 Map.Entry e1 = (Map.Entry) o1;
                 Map.Entry e2 = (Map.Entry) o2;
                 return ((Comparable) e1.getValue()).compareTo(e2.getValue());
             
         );

for (Map.Entry e : a) 
        System.out.println(e.getKey() + " " + e.getValue());

输出:

e 10
b 20
c 20
z 30

【讨论】:

【参考方案4】:
base.get(a) ==  base.get(b)

这段代码通过引用比较了装箱的Integers。

将其更改为base.get(a).equals(base.get(b)),它应该可以工作。

【讨论】:

我不认为这是真正的问题:) @TerryLi:不; == 运算符不会自动拆箱。

以上是关于Java Map 按值排序的主要内容,如果未能解决你的问题,请参考以下文章

java Java8 Map按值反转排序

Java 8 Map & Stream - 按值 desc 和组排序

降序排序:Java Map

如何先按值对 std::map 排序,然后按键排序?

c++ map基础知识、按键排序、按值排序

HashMap按键排序和按值排序