简体   繁体   English

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

[英]Sorting HashMap by value using a TreeMap and Comparator

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

Please check the JavaDoc of compare : You do not return the bigger value, but -1 for o1 < o2 , 0 for o1 = o2 and 1 for o1 > o2 . 请检查的JavaDoc的compare :你不回更大的价值,但-1o1 < o20o1 = o21o1 > o2 So you could write: 所以你可以这样写:

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

The Java Doc of TreeMap clearly states that: TreeMapJava文档明确指出:

A Red-Black tree based NavigableMap implementation. 基于红黑树的NavigableMap实现。 The map is sorted according to the natural ordering of its keys 该地图是根据其的自然顺序排序的

we should not violate this rule by using TreeMap to sort by values. 我们不应该通过使用TreeMap按值排序来违反此规则。

However to sort by values, we can do the following: 但是,要按值排序,我们可以执行以下操作:

  1. Create a LinkedList of entries of the map 创建map条目的LinkedList
  2. using Collection.sort to sort the entries 使用Collection.sort对条目进行排序
  3. Inserting the sorted entries to a LinkedHashMap : keeps the keys in the order they are inserted, which is currently sorted on natural ordering. 将排序后的条目插入LinkedHashMap :保持键的插入顺序,当前是按自然顺序排序的。
  4. Return the LinkedHashMap as the sorted map . 返回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 参考: 按值对地图排序

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. 在执行输出时,遍历每个元素的排序后的唯一词列表,从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%29 Both 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答案均不符合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. 缺少的部分应该没有什么困难。

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

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