简体   繁体   English

在HashMap中获取前N个键(按值排序)的最佳方法

[英]Best way to get top N keys(sorted by values) in a HashMap

The original data looks like this: 原始数据如下所示:

String data = "{ \"a\":1, \"b\":3 , \"c\":-1 }";

My first step is to convert it into a HashMap: 我的第一步是将其转换为HashMap:

Gson gson = new Gson();
HashMap<String, Double> map = gson.fromJson(data, HashMap.class);

And then sort the keys by their values: 然后按键值对键进行排序:

public static List<String> sortHashMap(final HashMap<String, Double> map) {
    Set<String> set = map.keySet();
    List<String> keys = new ArrayList<String>(set);

    Collections.sort(keys, new Comparator<String>() {

        @Override
        public int compare(String s1, String s2) {
            if (map.get(s1) < map.get(s2)) {
                return 1;
            }
            return 0;
        }
    });

    return keys;
}

At last, get top N keys: 最后,获得前N键:

keys.subList(0, N);

I finally get the result, but I don't think it's an elegant way. 我终于得到了结果,但我不认为这是一种优雅的方式。

So I wonder, is there any convenient way to make it ? 所以我想知道,有没有方便的方法来制作它?

A more elegant and scalable approach would be to use a priority queue where the size is limited to N. Using a min-heap priority queue, we can keep adding entries to the queue till the size reaches N. For each entry after the size of the priority queue has reached N, add it to the queue and then remove the element at the head of the queue (which will have the minimum value). 更优雅和可扩展的方法是使用优先级队列,其中大小限制为N.使用最小堆优先级队列,我们​​可以继续向队列添加条目,直到大小达到N.对于每个条目的大小优先级队列已达到N,将其添加到队列中,然后删除队列头部的元素(具有最小值)。 After we have exhausted all the entries from the HashMap, the queue will contain the Top N entries. 在我们耗尽了HashMap中的所有条目之后,队列将包含前N个条目。

The advantage of this approach is that even if the entire HashMap cannot fit in memory, we can break it into smaller blocks and use this approach. 这种方法的优点是即使整个HashMap无法适应内存,我们也可以将其分解为更小的块并使用这种方法。 Also, if we have a concurrent priority queue we can simultaneously add entries to the queue from different HashMaps as well. 此外,如果我们有一个并发优先级队列,我们​​也可以同时从不同的HashMaps向队列中添加条目。

public static List<String> topNKeys(final HashMap<String, Double> map, int n) {
    PriorityQueue<String> topN = new PriorityQueue<String>(n, new Comparator<String>() {
        public int compare(String s1, String s2) {
            return Double.compare(map.get(s1), map.get(s2));
        }
    });

    for(String key:map.keySet()){
        if (topN.size() < n)
            topN.add(key);
        else if (map.get(topN.peek()) < map.get(key)) {
            topN.poll();
            topN.add(key);
        }
    }
    return (List) Arrays.asList(topN.toArray());
}

What you've done is OK; 你所做的就是好的; you're going to have to write a custom Comparator somewhere, and where you've used it is fine. 你将不得不在某个地方写一个自定义比较器,你使用它的地方很好。

But you have a bug in your compare() method: You are returning 0 if s1 > s2, but you should only do that if the numbers are equal and return a negative number if s1 > s2. 但是你的compare()方法有一个错误 :如果s1> s2你返回0,但是你应该只在数字相等的情况下返回0,如果s1> s2则返回负数。 The below implementation corrects that. 以下实现纠正了这一点。

A better (and simpler) implementation is: 更好(更简单)的实现是:

 public int compare(String s1, String s2) {
     return Double.compare(map.get(s2), map.get(s1)); //reverse order
 }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM