Alexandr
Alexandr

Reputation: 726

Sort java map by value (which contains a set)

I have a map which looks like

Map<Word, Set<Word>> tupleMap = new HashMap<Word, Set<Word>>();

The Word class is nothing special, it contains a few Strings like value and so on. I am trying to sort this map by the number of elements of the associated set. So the Word which has most words associated to it will be first/last in map.

I did manage to sort this map by its Key using this code:

WordMapComparator bvc = new WordMapComparator(tupleMap);
TreeMap<Word, Set<Word>> sortedTupleMap = new TreeMap<Word, Set<Word>>(bvc);
    sortedTupleMap.putAll(tupleMap); `

where the comparator is

class WordMapComparator implements Comparator<Word> {

    Map<Word, Set<Word>> base;

    public WordMapComparator(Map<Word, Set<Word>> tupleMap) {
        this.base = tupleMap;
    }

    public int compare(Word a, Word b) {
        return a.getValue().compareTo(b.getValue());
    }
}

Now this works just fine, so map gets sorted based on the words value. However, I tried this comparator:

class WordMapComparator implements Comparator<Word> {

    Map<Word, Set<Word>> base;

    public WordMapComparator(Map<Word, Set<Word>> tupleMap) {
        this.base = tupleMap;
    }

    public int compare(Word a, Word b) {
        return base.get(a).size() >= base.get(b).size() ? -1 : 1;
    }
}

and testing the resulting map with

    1. for(Word w : sortedTupleMap.keySet()){
    2.  System.out.print(w.getValue() + " : ");
    3.  for(Word wo : sortedTupleMap.get(w)){
    4.      System.out.print(wo.getValue() + " ");
    5.  }
    6.  System.out.println();
    7. }    

I get a null pointer exception at line 3, basically, the resulting Set is null.

Why does this happen, and moreover, how can this be fixed?

Thanks a million!

Upvotes: 0

Views: 90

Answers (1)

JB Nizet
JB Nizet

Reputation: 691735

return base.get(a).size() >= base.get(b).size() ? -1 : 1;

This is clearly wrong: if the number of words is the same, it should return 0, not 1 or -1 depending on how a and b are compared. Just use

return Integer.compare(base.get(a).size(), base.get(b).size());

That, however, will make the map consider all the words with the same amount of associated words as equal, so you need to distinguish them by another criterion:

int result = Integer.compare(base.get(a).size(), base.get(b).size());
if (result == 0) {
    result = a.compareTo(b);
}
return result;

Upvotes: 2

Related Questions