14

So I am very new to Java and as such I'm fighting my way through an exercise, converting one of my Python programs to Java.

I have run into an issue where I am trying to replicate the behavior, from python the following will return only the keys sorted (by values), not the values:

popular_numbers = sorted(number_dict, key = number_dict.get, reverse = True)

In Java, I have done a bit of research and have not yet found an easy enough sample for a n00b such as myself or a comparable method. I have found examples using Guava for sorting, but the sort appears to return a HashMap sorted by key.

In addition to the above, one of the other nice things about Python, that I have not found in Java is the ability to, easily, return a subset of the sorted values. In Python I can simply do the following:

print "Top 10 Numbers: %s" % popular_numbers[:10]

In this example, number_dict is a dictionary of key,value pairs where key represents numbers 1..100 and the value is the number of times the number (key) occurs:

for n in numbers:
 if not n == '':
   number_dict[n] += 1

The end result would be something like:

Top 10 Numbers: ['27', '11', '5', '8', '16', '25', '1', '24', '32', '20']

To clarify, in Java I have successfully created a HashMap, I have successfully examined numbers and increased the values of the key,value pair. I am now stuck at the sort and return the top 10 numbers (keys) based on value.

Eric
  • 590
  • 2
  • 6
  • 18
  • 3
    Is there a reason for using `HashMap`, particularly since you need sorting? – Zong Jun 13 '13 at 19:09
  • Have you thought about using a tree map rather than a hash map? – BlackHatSamurai Jun 13 '13 at 19:12
  • Possible duplicate: http://stackoverflow.com/questions/780541/how-to-sort-hash-map , http://stackoverflow.com/questions/8119366/sorting-hashmap-by-values – BlackHatSamurai Jun 13 '13 at 19:13
  • @TheNewIdiot well, that sorts the keys, not the values – darijan Jun 13 '13 at 19:13
  • "top 10 numbers based on value"? What do you mean? – fge Jun 13 '13 at 19:14
  • No particular reason, HashMap is what I found that most closely resembled a Python Dictionary. I'm open to suggestions. – Eric Jun 14 '13 at 19:08
  • Top 10 numbers refers to the numbers (keys) who have occurred most frequently in a list of numbers read in. So for a very basic example, I initialize a dictionary of sorts, {1: 0, 2: 0, 3: 0}. Given a list of numbers [3,2,2,3,1,2] I would have at the end {1: 1, 2: 3, 3: 2}. When sorted by values I would have popular_numbers = [2, 3, 1]. – Eric Jun 14 '13 at 19:11

9 Answers9

10
  1. Put the map's entrySet() into a List.
  2. Sort this list using Collections.sort and a Comparator which sorts Entrys based on their values.
  3. Use the subList(int, int) method of List to retrieve a new list containing the top 10 elements.

Yes, it will be much more verbose than Python :)

arshajii
  • 118,519
  • 22
  • 219
  • 270
  • Yeah this is the way to go. But then what was the point of using `HashMap`? Especially when the result is ordered by *value*? I don't know what the OP is doing... – Zong Jun 13 '13 at 19:17
2

With Java 8+, to get the first 10 elements of a list of intergers:

list.stream().sorted().limit(10).collect(Collectors.toList());

To get the first 10 elements of a map's keys, that are integers:

map.keySet().stream().sorted().limit(10).collect(Collectors.toMap(Function.identity(), map::get));
i000174
  • 633
  • 6
  • 12
0

Assuming your map is defined something like this and that you want to sort based on values:

HashMap<Integer, Integer> map= new HashMap<Integer, Integer>();
//add values
Collection<Integer> values= map.values();
ArrayList<Integer> list= new ArrayList<Integer>(values);
Collections.sort(list);

Now, print the first top 10 elements of the list.

for (int i=0; i<10; i++) {
    System.out.println(list.get(i));
}

The values in the map are not actually sorted, because the HashMap is not sorted at all (it stores the values in the buckets based on the hashCode of the key). This code is just displaying 10 smallest elements in the map.

EDIT sort without loosing the key-value pairs:

//sorted tree map
TreeMap<Integer, Integer> tree= new TreeMap<>();

//iterate over a map
Iteartor<Integer> it= map.keySet().iterator();
while (it.hasNext()) {
    Integer key= it.next();
    tree.put(map.get(key), key);
}

Now you have the TreeMap tree that is sorted and has reversed key-value pairs from the original map, so you don't lose the information.

darijan
  • 9,449
  • 23
  • 37
0

HashMaps aren't ordered in Java, and so there isn't really a good way to order them short of a brute-force search through all the keys. Try using TreeMap: http://docs.oracle.com/javase/6/docs/api/java/util/TreeMap.html

chessbot
  • 426
  • 2
  • 11
0

Try the next:

public static void main(String[] args) {

    // Map for store the numbers
    Map<Integer, Integer> map = new HashMap<Integer, Integer>();

    // Populate the map ...

    // Sort by the more popular number
    Set<Entry<Integer, Integer>> set = map.entrySet();
    List<Entry<Integer, Integer>> list = new ArrayList<>(set);
    Collections.sort(list, new Comparator<Entry<Integer, Integer>>() {
        @Override
        public int compare(Entry<Integer, Integer> a,
                Entry<Integer, Integer> b) {
            return b.getValue() - a.getValue();
        }
    });


    // Output the top 10 numbers
    for (int i = 0; i < 10 && i < list.size(); i++) {
        System.out.println(list.get(i));
    }

}
Paul Vargas
  • 38,878
  • 15
  • 91
  • 139
  • It would be better to use `b.getValue().compareTo(a.getValue())` in the comparison. The OP's example uses integers, but if they decided to put some floating points in, just changing the types would break this code. – Zong Jun 13 '13 at 19:25
  • The `value` is the **popularity** of a number (number may be floating). In other words, the **frequency**. – Paul Vargas Jun 13 '13 at 19:27
  • Alright, I see that in this case it will just be integers. However, I'd still stick with my suggestion. – Zong Jun 13 '13 at 19:31
0

Guava Multiset is a great fit for your use case, and would nicely replace your HashMap. It is a collection which counts the number of occurences of each element.

Multisets has a method copyHighestCountFirst, which returns an immutable Multiset ordered by count.

Now some code:

Multiset<Integer> counter = HashMultiset.create();
//add Integers 
ImmutableMultiset<Integer> sortedCount = Multisets.copyHighestCountFirst(counter);
//iterate through sortedCount as needed
0

Use a SortedMap, call values(). The docs indicate the following:

The collection's iterator returns the values in ascending order of the corresponding keys

So as long as your comparator is written correctly you can just iterate over the first n keys

nsfyn55
  • 13,511
  • 7
  • 45
  • 75
0
  1. Build a list from the keyset.

  2. Sort the HashMap by values using the keys to access the value in the Collection.sort() method.

  3. Return a sub list of the sorted key set.

  4. if you care about the values, you can use the keys in step 3 and build value set.

    HashMap<String, Integer> hashMap = new HashMap<String, Integer>(); List list = new ArrayList(hashMap.keySet()); Collections.sort(list, (w1, w2) -> hashMap.get(w2) - hashMap.get(w1)); //sorted descending order by value;

    return list.subList(0, 10);

solxget
  • 1
  • 1
0

To preserve the ranking order and efficiently return top count, much smaller than the size of the map size:

map.entrySet().stream()
            .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
            .limit(count)
            .collect(toMap(Map.Entry::getKey, Map.Entry::getValue,
                    (e1, e2) -> e1,
                    LinkedHashMap::new))
kisna
  • 2,189
  • 1
  • 22
  • 27