Reputation: 45351
I was reading this article and tried counting some words in a text file and found I could not reverse sort similarly to how it showed in listing 1 of the article.
I have some code that works though:
public class WordCounter {
public static final PrintWriter out = new PrintWriter(System.out, true);
public static void main(String... args) throws IOException {
//The need to put "", in front of args in the next line is frustrating.
try (Stream<String> lines = Files.lines(Paths.get("", args))) {
lines.parallel()
.map(l -> l.toLowerCase().replaceAll("[^a-z\\s]", "").split("\\s"))
.flatMap(Arrays::stream)
.filter(s -> !s.isEmpty())
.collect(Collectors.groupingBy(
Function.identity(), Collectors.counting()))
// Sort Map<K,V> Entries by their Integer value descending
.entrySet().parallelStream()
// MY QUESTION IS ABOUT THIS METHOD:
.sorted(
Comparator.comparing(Map.Entry::getValue, Comparator.reverseOrder()))
// --------------------------------- //
.forEachOrdered(e -> out.printf("%5d\t%s\n", e.getValue(), e.getKey()));
}
out.close();
}
}
So the article would suggest that the line:
.sorted(Comparator.comparing(Map.Entry::getValue, Comparator.reverseOrder()))
could be written as:
.sorted(Comparator.comparing(Map.Entry::getValue).reversed())
For this though, the Java compiler complains that:
Error:(46, 49) java: invalid method reference non-static method getValue() cannot be referenced from a static context
The two comparing
method signatures have the exact same first parameter and static scope, yet the former works while the latter complains about getValue
being non-static.
My original thought was to write it as either:
.sorted(Map.Entry.comparingByValue())
Which compiles and runs but is not reversed. Or as:
.sorted(Map.Entry.comparingByValue().reversed())
Which again doesn't compile, giving an error message of:
Error:(48, 62) java: incompatible types: java.util.Comparator<java.util.Map.Entry<java.lang.Object,V>> cannot be converted to java.util.Comparator<? super java.util.Map.Entry<java.lang.String,java.lang.Long>>
Okay, so, that should be:
.sorted(Map.Entry.<String, Long>comparingByValue().reversed())
Which works.
I can't seem to see how to give a similar generic type specification to the Map.Entry::getValue
form in my "could be written as" line though.
Upvotes: 3
Views: 853
Reputation: 37855
As to why this happens: while type inference has come leaps and bounds in Java 8, it will still only use the return target type if the return value is assigned to something.
In Java 7 we were only able to use this in an assignment context (using =
) and it was a little bit clunky. In Java 8, it's less clunky and we can use it in invocation contexts (passed as a method argument, which assigns it to the formal parameter).
So the way I understand it, if the method invocation isn't used in an assignment context or invocation context, target type inference simply turns off, because it's no longer something called a poly expression (15.12, 18.5.2). So says the JLS.
In short, target type inference only works if the return value is:
=
, as in v = foo();
.bar(foo())
.Once you chain a method call in, like v = foo().zap()
, it stops working.
Lifted from my comment:
I can't seem to see how to give a similar generic type specification to the
Map.Entry::getValue
form though.
This would be Map.Entry<String, Long>::getValue
.
Upvotes: 3