Search code examples
javatreemap

How to create a TreeMap with a comparator based on "external" values


I have a Map<String, Integer> otherMap which maps properties of strings to some associated integer values.

Now I would like a TreeMap<String, String> that orders the keys according to the associated integers in otherMap.

How should I tackle this problem, and what's important to keep in mind?

(This is a follow-up on this question.)


Solution

  • When writing a comparator it's important to ensure that the results are consistent (i.e. are the same over time) and that it implements a total order.

    When using the comparator in a TreeMap it is also required that it is consistent with equals, which means that c.compare(e1, e2) return 0 if and only if e1.equals(e2).

    With this in mind, a correct comparator could be implemented as follows:

    class MyComparator implements Comparator<String> {
    
        Map<String, Integer> otherMap;
    
        public MyComparator(Map<String, Integer> otherMap) {
            this.otherMap = otherMap;
        }
    
        @Override
        public int compare(String o1, String o2) {
            int primary = otherMap.get(o1).compareTo(otherMap.get(o2));
            if (primary != 0)
                return primary;
            // Fall back on comparing the string keys to ensure consistent results
            return o1.compareTo(o2);
        }
    }
    

    (It should be noted that it is also important that otherMap never changes after it is passed to MyComparator.)


    In Java 8, the idiomatic solution would look like

    Comparator.comparing(k -> otherMap.get(k))
              .thenComparing(k -> k);