Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java - Inline definition of comparator for Collection.max

Tags:

java

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!

like image 981
Nullpoet Avatar asked Nov 05 '13 09:11

Nullpoet


2 Answers

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.

like image 130
Mike Strobel Avatar answered Nov 15 '22 04:11

Mike Strobel


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();
}
like image 4
Holger Avatar answered Nov 15 '22 04:11

Holger