15

I have a list of commmons Pair that stores words and their frequency like the following

private List<Pair<String, Integer>> words = new ArrayList<Pair<String, Integer>();

I am trying to sort it so that when I iterate over it to print the words, I want the words with the highest frequency to appear first.

I tried playing around with implementing Comparable but most examples are not similar to using a list of Pairs

124697
  • 22,097
  • 68
  • 188
  • 315
  • 1
    You should be able to use this: http://stackoverflow.com/questions/16252269/how-to-sort-a-list-arraylist-in-java – Chris Bolton Apr 28 '15 at 12:52
  • 1
    Don't you think it's better to define a `Pair` class with word and its frequency instead of using the `Pair` structure from commons. That way you can simply create a custom `Comparator` to define the sorting criteria based on the word (or) frequency – Arkantos Apr 28 '15 at 12:55
  • 1
    Why don't you use a Map? Map wordsFrequencyMap; – ACV Apr 28 '15 at 12:56

4 Answers4

25

To sort the elements by decreasing order of number

Collections.sort(words, Comparator.comparing(p -> -p.getRight()));

This will use the "right" of the pair in descending order.

This uses Java 8. Notionally you are boxing the value and using Integer.compareTo.

However, with escape analysis, the boxing can be eliminated and you mgiht not be creating any objects.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
20

You can use a custom Comparator:

Collections.sort(words, new Comparator<Pair<String, Integer>>() {
    @Override
    public int compare(final Pair<String, Integer> o1, final Pair<String, Integer> o2) {
        // TODO: implement your logic here
    }
});
3

Hello i think this should work for you.

 List<Pair<String, Integer>> words = new ArrayList<Pair<String, Integer>>();
    words.add(new Pair<String, Integer>("hello",2));
    words.add(new Pair<String, Integer>("hello",1));
    words.add(new Pair<String, Integer>("aello",3));

    words.sort(new Comparator<Pair<String, Integer>>() {
        @Override
        public int compare(Pair<String, Integer> o1, Pair<String, Integer> o2) {
            if (o1.getValue() > o2.getValue()) {
                return -1;
            } else if (o1.getValue().equals(o2.getValue())) {
                return 0; // You can change this to make it then look at the
                          //words alphabetical order
            } else {
                return 1;
            }
        }
    });

    System.out.println(words);
Greg King
  • 140
  • 1
  • 9
  • 2
    don't you think that relying on compareTo() provided by Integer would be better instead of rewriting it yourself? – guido Apr 28 '15 at 12:59
  • Sure but if you want to further sort the values alphabetically if the frequency is the same then it doesnt make much difference . But if you aren't, returning Integer.compare(o1.getValue() ,o2.getValue()); is easier yes. – Greg King Apr 28 '15 at 13:04
  • 2
    @GregKing Or you _could_ use [`Comparator.thenComparing`](https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html#thenComparing-java.util.function.Function-java.util.Comparator-). Depends how much redundant code you want to write I suppose. – Boris the Spider Apr 28 '15 at 13:07
3

Use a Java 8 lambda in combination with Comparator.comparing (you also need to reverse the order):

import static java.util.Collections.reverseOrder;
import static java.util.Comparator.comparing;

final List<Pair<String, Integer>> words = new ArrayList<>();
final Comparator<Pair<String, Integer>> c = reverseOrder(comparing(Pair::getValue));
Collections.sort(words, c);

Easiest way if you just want to print the values in descending order of frequency:

words.stream()
        .sorted(c)
        .map(Pair::getKey)
        .forEach(System.out::println);
Boris the Spider
  • 59,842
  • 6
  • 106
  • 166