Nullpoet
Nullpoet

Reputation: 11259

Java - Inline definition of comparator for Collection.max

I am looking for a Java equivalent for python snippet

max_valued_key = max(str_to_double_map.keys(), key=lambda x: str_to_double_map[x])

I want to something standard like Collections.max
Is there a way to do this with inline definition of Comparator since I don't want to write one more class for every other comparator.

I tried following code unsuccessfully

depScores = foo();
String dep = Collections.max(depScores.keySet(), new Comparator<String>() {
  @Override
  public int compare(String o1, String o2) {
    return depScores.get(o1).compareTo(depScores.get(o2));
  }
});

depScores variable is not readable from comparator.
Looks like in java inner class cannot access non-final variable from outside!

Thanks in advance!

Upvotes: 7

Views: 11873

Answers (2)

Mike Strobel
Mike Strobel

Reputation: 25623

Just declare depScores as a final variable. If for some reason you can't, create a second (final) variable that points to it.

Local classes can capture variables only if they are final.


As a (very) late addendum, it is trivial to create a custom Comparator from a lambda in Java 8:

String dep = Collections.max(
    depScores.keySet(),
    Comparator.comparing(k -> depScores.get(k))
);

You can get even more terse by replacing the lambda k -> depScores.get(k) with the method reference depScores::get.

The rules for capturing local variables like depScore are a little more flexible for lambdas than inner classes: captured variables need only be effectively final. In other words, they must be assigned exactly once, though they needn't be explicitly marked final.

Upvotes: 4

Holger
Holger

Reputation: 298233

What you want is (will be) possible with Java 8:

Map<String,Double> map…
String maxKey=Collections.max(map.keySet(), (x,y)->Double.compare(map.get(x),map.get(y)));

or even shorter

String maxKey = Collections.max(map.keySet(), Comparator.comparingDouble(map::get));

For previous Java version you have to use:

String maxKey=Collections.max(map.keySet(), new Comparator<String>(){
    public int compare(String x, String y) {
        return Double.compare(map.get(x),map.get(y));
    }
});

Problems with map not being final can be circumvented by assigning it to a final variable right before the invocation:

final Map<String,Double> fmap=map;
String maxKey=Collections.max(map.keySet(), new Comparator<String>(){
    public int compare(String x, String y) {
        return Double.compare(fmap.get(x),fmap.get(y));
    }
});

But I think even more straightforward and more efficient will be the following helper method as it does not require any hash lookups:

static <K,V extends Comparable<V>> K keyForHighestValue(Map<K,V> map) {
    V maxValue=Collections.max(map.values());
    for(Map.Entry<K,V> e:map.entrySet()) {
        if(e.getValue()==maxValue) return e.getKey();
    }
    throw new ConcurrentModificationException();
}

Upvotes: 4

Related Questions