Java 使用 TreeMap 和 Comparator 按值对 HashMap 进行排序

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19579923/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 18:29:59  来源:igfitidea点击:

Sorting HashMap by value using a TreeMap and Comparator

javasortingcollectionshashmap

提问by seeker

Im using the following code to create a hashmap and then sort the values in the hashmap by using a treemap and a comparator. However, the output is rather unexpected. So any thoughts as to what Im doing wrong would be helpful

我使用以下代码创建哈希图,然后使用树图和比较器对哈希图中的值进行排序。然而,输出是相当出乎意料的。所以任何关于我做错了什么的想法都会有所帮助

Code

代码

public static void main(String[] args) {
    System.out.println("Most freq"+mostFreq(" i me hello hello hello me"));
}


public static String[] mostFreq(String str){

    if ((str==null)||( str.trim().equalsIgnoreCase("")))
        return null;

    String[] arr = new String[10];

    String[] words= str.split(" ");

    Map <String,Integer> map = new HashMap<String,Integer>();

    for (String word :words)
    { 
        int count =0;
        if (map.containsKey(word))
        {     
            count= map.get(word);
            map.put(word, count+1);
        }             
        else
            map.put(word, 1);
    }

    MyComparator comp= new MyComparator(map);
    Map<String,Integer> newMap= new TreeMap(comp);
    newMap.putAll(map);
    Iterator it= newMap.entrySet().iterator();
    while (it.hasNext())
    {
        Map.Entry pairs = (Map.Entry) it.next();
        System.out.println("Key  "+pairs.getKey()+"-- value"+pairs.getValue());
    }

    return arr;
}

Here's the comparator

这是比较器

package samplecodes;

import java.util.Comparator;
import java.util.Map;

public class MyComparator implements Comparator {

    Map map;

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

    @Override
    public int compare(Object o1, Object o2) {
        return ((Integer)map.get(o1) >(Integer)map.get(o2)? (Integer)map.get(o1):(Integer)map.get(o2));
    }

}

And the output is of the form

和输出的形式

me-2
hello-3
i-3

回答by Robin Krahl

Please check the JavaDoc of compare: You do not return the bigger value, but -1for o1< o2, 0for o1= o2and 1for o1> o2. So you could write:

请检查的JavaDoc的compare:你不回更大的价值,但-1对于o1< o20用于o1=o21o1> o2。所以你可以写:

@Override
public int compare(Object o1, Object o2) {
    return ((Integer) map.get(o1)).compareTo((Integer) map.get(o2);
}

回答by Sage

The Java Docof TreeMapclearly states that:

Java文档TreeMap明确指出:

A Red-Black tree based NavigableMap implementation. The map is sorted according to the natural ordering of its keys

基于红黑树的 NavigableMap 实现。地图根据其的自然顺序进行排序

we should not violate this rule by using TreeMapto sort by values.

我们不应该使用TreeMap按值排序来违反此规则。

However to sort by values, we can do the following:

但是要按值排序,我们可以执行以下操作:

  1. Create a LinkedListof entries of the map
  2. using Collection.sortto sort the entries
  3. Inserting the sorted entries to a LinkedHashMap: keeps the keys in the order they are inserted, which is currently sorted on natural ordering.
  4. Return the LinkedHashMapas the sorted map.

     public static <K extends Comparable,V extends Comparable> Map<K,V> sortByValues(Map<K,V> map){
        List<Map.Entry<K,V>> entries = new LinkedList<Map.Entry<K,V>>(map.entrySet());
    
        Collections.sort(entries, new Comparator<Map.Entry<K,V>>() {
    
            @Override
            public int compare(Entry<K, V> o1, Entry<K, V> o2) {
                return o1.getValue().compareTo(o2.getValue());
            }
        });
    
    
        Map<K,V> sortedMap = new LinkedHashMap<K,V>();
    
        for(Map.Entry<K,V> entry: entries){
            sortedMap.put(entry.getKey(), entry.getValue());
        }
    
        return sortedMap;
    }
    
    }
    
  1. 创建一个LinkedList条目map
  2. 使用Collection.sort的条目进行排序
  3. 将已排序的条目插入到 a LinkedHashMap: 保持键的插入顺序,当前按自然顺序排序。
  4. 返回LinkedHashMap排序后的map

     public static <K extends Comparable,V extends Comparable> Map<K,V> sortByValues(Map<K,V> map){
        List<Map.Entry<K,V>> entries = new LinkedList<Map.Entry<K,V>>(map.entrySet());
    
        Collections.sort(entries, new Comparator<Map.Entry<K,V>>() {
    
            @Override
            public int compare(Entry<K, V> o1, Entry<K, V> o2) {
                return o1.getValue().compareTo(o2.getValue());
            }
        });
    
    
        Map<K,V> sortedMap = new LinkedHashMap<K,V>();
    
        for(Map.Entry<K,V> entry: entries){
            sortedMap.put(entry.getKey(), entry.getValue());
        }
    
        return sortedMap;
    }
    
    }
    

Reference:Sorting Map by value

参考:按值排序地图

回答by Adrian Shum

What you are doing is really a misuse of tools.

您所做的实际上是滥用工具。

I believe what you need to do is:

我相信你需要做的是:

  1. Have a list/array of input words (still fine that you get it by splitting the input string)
  2. Create a Map to store the word as key, and frequency as value
  3. Have a collection of unique words, then sort the collection base on the the frequency
  4. When you are doing the output, traverse the sorted unique word list, for each element, get the frequency from the frequencyMap, and output the word + frequency.
  1. 有一个输入单词的列表/数组(通过拆分输入字符串来获得它仍然很好)
  2. 创建一个 Map 以将单词存储为键,将频率存储为值
  3. 有一个独特的词的集合,然后根据频率对集合进行排序
  4. 做输出的时候,遍历排序后的唯一词列表,对于每个元素,从frequencyMap中获取频率,输出词+频率。

Of course you can still make use of something like a TreeSet and use frequency as key, but you should have list of words as the value of this map (aka Multi-Map), instead of writing a problematic comparator which do not follow the contract of Comparator: http://docs.oracle.com/javase/6/docs/api/java/util/Comparator.html#compare%28T,%20T%29Both your original implementation and the one in comment of one of the answers does not comply with the rule of sgn(compare(x, y)) == -sgn(compare(y, x)) for all x and y(The original one is even worse).

当然,您仍然可以使用 TreeSet 之类的东西并使用频率作为键,但是您应该将单词列表作为此映射(又名 Multi-Map)的值,而不是编写一个不遵守约定的有问题的比较器比较器的:http: //docs.oracle.com/javase/6/docs/api/java/util/Comparator.html#compare%28T,%20T%29您的原始实现和其中之一的评论中的一个答案不符合规则sgn(compare(x, y)) == -sgn(compare(y, x)) for all x and y(原来的更糟)。

some code snippet just for giving you hints:

一些代码片段只是为了给你提示:

List<String> words = ....;
Map<String, Integer> wordFrequencyMap = new HashMap<String, Integer>();
// iterate words and update wordFrequencyMap accordingly
List<String> uniqueWords = new ArrayList<String>(new HashSet<String>(words));
Collections.sort(uniqueWords, new WordFrequencyComparator<String>(wordFrequencyMap));
for (String w : uniqueWords) {
  System.out.println("word : " + w + "  frequency : " + wordFrequencyMap.get(w));
}

The missing part shouldn't be anything difficult.

缺失的部分应该不难。