Java 8 predicates using methods bodies are called only once?

落爺英雄遲暮 提交于 2021-01-19 06:03:14

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!