问题
I have examined the following snippet:
public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Map<Object, Boolean> computed = new ConcurrentHashMap<>();/*IS THIS LINE CALLED ONCE ON STREAM->FILTER NOT MATTER HOW LONG THE STREAM IS*/
return t -> {return computed.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;};
}
private void test(){
final long d = Stream.of("JOHN","STEPHEN","ORTIZ","RONDON")
.filter(distinctByKey(Function.identity()))
.count();
System.out.println("d = " + d);
}
This code is not mine. I know that using a ConcurrentMap
is not the right choice in this example and I should use ConcurrentMap
instead of Map
in this case, but this is not my concern now.
I thought that the distinctByKey
method is called or interpreted in each iteration of the Stream
. I mean the Map
being instantiated in each turn, but it's not!
Is the body of the Predicate
method called only once?
In the Stream
iteration, is this an assertion?
Because when I try the following code:
final Function<String,Integer>a = (name)->name.length();
System.out.println(distinctByKey(a).test("JOHN"));
System.out.println(distinctByKey(a).test("STEPHEN"));
System.out.println(distinctByKey(a).test("ORTIZ"));
System.out.println(distinctByKey(a).test("RONDON"));
I can see that the body of the method is indeed called in each line. What makes the body of the filter to only be called once?
回答1:
When you call .filter(distinctByKey(Function.identity()))
, the argument passed to filter()
is evaluated. That's the only time distinctByKey(Function.identity())
is executed and returns an instance of Predicate<String>
.
That Predicate
is then evaluated (i.e. it's test()
method is executed) multiple times, each time for a different element of the Stream
.
To make your last snippet behave similar to the Stream
pipeline, it should look like this:
final Function<String,Integer> a = (name)->name.length();
Predicate<String> pred = distinctByKey(a);
System.out.println(pred.test("JOHN"));
System.out.println(pred.test("STEPHEN"));
System.out.println(pred.test("ORTIZ"));
System.out.println(pred.test("RONDON"));
回答2:
distinctByKey
returns a single instance of the Predicate
that caches the ConcurrentHashMap
. You could achieve almost the same thing if you replace the creation of the Predicate
via the lambda with an anonymous inner class for example.
回答3:
I thought that the distinctByKey method is called or interpreted in each iteration of the Stream i mean the Map being instance in each turn but it's not! my question is the body of the Predicate method call only one time? in the Stream iteration is this a assertion?
No. Streams are not magic, and they do not overthrow standard Java semantics. Consider the code presented:
final long d = Stream.of("JOHN","STEPHEN","ORTIZ","RONDON") .filter(distinctByKey(Function.identity())) .count();
Taking specific types and methods out of the picture, that has this general form:
long x = A.b(y).c(z).d(w);
There's no reason to expect that any of a()
, b()
, or c()
is invoked more than once in that chain, and or that their arguments are evaluated more than once each. That is not affected by some of the types being Stream
.
What happens instead in your case is that the Predicate
returned by (the sole invocation of) your distinctByKey()
method is used more than once as the stream in which it is embedded is processed. That Predicate
contains a reference to a Map
, which it uses and modifies in performing its work.
来源:https://stackoverflow.com/questions/56326648/java-8-predicates-using-methods-bodies-are-called-only-once