-1

I am creating a word frequency program which works good with counting the words. I am using a hashmap to store the word frequencies. I use the word as the key and the count as the value. However, I want to sort the hashMap at the end first by the word frequency and then by key alphabetically like this:

it - 2
of - 2
the - 2
times- 2 
was - 2  
best- 1 
worst - 1 

I am guessing that I will need a custom Comparator, but I have no idea how to construct it. Please, any suggestions?

LBes
  • 3,366
  • 1
  • 32
  • 66
Georgi Koemdzhiev
  • 11,421
  • 18
  • 62
  • 126
  • 2
    You can't, but try searching for "[java sort hashmap](http://stackoverflow.com/search?q=java+sort+hashmap)" to find a bunch of other questions asking exactly the same question. – Andreas Oct 24 '15 at 00:15
  • 1
    @Andreas - I'm not sure it is seeing as he wants to sort based upon the value *and* the key – Catchwa Oct 24 '15 at 00:25

2 Answers2

4

You cannot have a sorted HashMap, as is stated in the Javadoc:

... This class makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time.

You will have to put it into a different structure that maintains the order you want it to.

Based on what you provided, it looks like you have a few criteria for your order:

  1. sorted by the Map.Entry value, in descending order
  2. sorted by the Map.Entry key, in ascending order

You can make use of the Stream API, along with the useful Comparators from Map.Entry.

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

map.put("it", 2);
map.put("of", 2);
map.put("the", 2);
map.put("times", 2);
map.put("was", 2);
map.put("best", 1);
map.put("worst", 1);

System.out.println("No Guaranteed Order:");
map.entrySet().stream()
        .forEach(System.out::println);


System.out.println();
System.out.println("With Ordering:");
map.entrySet().stream()
        .sorted(Map.Entry.<String, Integer>comparingByValue()
                        .reversed()
                .thenComparing(Map.Entry.comparingByKey()))
        .forEach(System.out::println);

And the output:

No Guaranteed Order:
the=2
times=2
of=2
was=2
best=1
worst=1
it=2

With Ordering:
it=2
of=2
the=2
times=2
was=2
best=1
worst=1
mkobit
  • 43,979
  • 12
  • 156
  • 150
2

Seeing as you're only needing to do this sort once, I'd suggest that you create a Result object that holds your String and Integer values and implements Comparable (keep the key the same). When you need to sort your results, just call values() on the HashMap and then sort them.

Main.java

import java.util.*;

class Main
{
    public static void main (String[] args) throws java.lang.Exception
    {
        HashMap<String, Result> hm = new HashMap<String, Result>();
        addTo(hm, "to");
        addTo(hm, "be");
        addTo(hm, "or");
        addTo(hm, "not");
        addTo(hm, "to");
        addTo(hm, "be");

        List<Result> results = new ArrayList(hm.values());
        Collections.sort(results);
        for(Result result : results)
        {
            System.out.println(result.getValue() + " - " +result.getCount());
        }       
    }

    public static void addTo(HashMap<String, Result> hm, String entry)
    {
        if(! hm.containsKey(entry))
        {
            Result result = new Result(entry);
            hm.put(entry, new Result(entry));
        }
        hm.get(entry).increment();
    }
}

Result.java

public class Result implements Comparable
    {
        private int count;
        private String value;

        public Result(String value)
        {
            this.value = value;
            this.count = 0;
        }

        public String getValue()
        {
            return value;
        }

        public int getCount()
        {
            return count;
        }

        public void increment()
        {
            count++;
        }

        @Override
        public int compareTo(Object o)
        {
            Result result = (Result)o;
            if(getCount() == result.getCount())
            {
                return getValue().compareTo(result.getValue());
            }
            else
            {
                // Want larger values to be on-top, so reverse traditional sort order
                return ((Integer)result.getCount()).compareTo((Integer)getCount());
            }
        }
    }
Catchwa
  • 5,845
  • 4
  • 31
  • 57