Java 8 Java8中谓词如何维护状态

Java 8 Java8中谓词如何维护状态,java-8,java-stream,Java 8,Java Stream,我正在看这篇文章,并试图理解下面的代码 抄袭斯图尔特·马克斯的答案 public static Predicate distinctByKey(函数因为这是一个捕获lambda,实际上每次调用distinctByKey时都会返回一个新的Predicate实例;但这会发生在整个流中,而不是每个单独的元素 如果您愿意以以下方式运行示例: Djdk.internal.lambda.dumpProxyClasses=/Your/Path/Here 您将看到为您的谓词的实现生成了一个类。因为这是一个有状态

我正在看这篇文章,并试图理解下面的代码

抄袭斯图尔特·马克斯的答案
public static Predicate distinctByKey(函数因为这是一个捕获lambda,实际上每次调用
distinctByKey
时都会返回一个新的
Predicate
实例;但这会发生在整个流中,而不是每个单独的元素

如果您愿意以以下方式运行示例:

Djdk.internal.lambda.dumpProxyClasses=/Your/Path/Here

您将看到为您的
谓词的实现生成了一个
。因为这是一个有状态lambda-它捕获
CHM
函数
,它将有一个
私有
构造函数和一个返回实例的
静态工厂方法


distinctByKey
的每次调用都将生成不同的实例,但该实例将被流的每个元素重用。如果运行此示例,事情可能会更明显:

 public static <T> Predicate<T> distinctByKey(Function<? super T,Object> keyExtractor) {
    Map<Object,Boolean> seen = new ConcurrentHashMap<>();
    Predicate<T> predicate =  t -> {
        Object obj = keyExtractor.apply(t);
        System.out.println("stream element : " + obj);
        return seen.putIfAbsent(obj, Boolean.TRUE) == null;
    };

    System.out.println("Predicate with hash :" + predicate.hashCode());
    return predicate;
}

@Getter
@AllArgsConstructor
static class User {
    private final String name;
}

public static void main(String[] args) {
    Stream.of(new User("a"), new User("b"))
          .filter(distinctByKey(User::getName))
          .collect(Collectors.toList());

}
流的两个元素都有一个
谓词

如果添加另一个
过滤器

Stream.of(new User("a"), new User("b"))
      .filter(distinctByKey(User::getName))
      .filter(distinctByKey(User::getName))
      .collect(Collectors.toList());
将有两个
谓词
s:

Predicate with hash :1259475182
Predicate with hash :1072591677
stream element : a
stream element : a
stream element : b
stream element : b

这看起来很混乱,但很简单。实际发生的是,
distinctByKey
方法只被调用一次,因此只有一个
ConcurrentHashMap
的实例被lambda表达式捕获。因此当
distinctByKey
方法返回一个
谓词时ct然后我们将其应用于流的每个元素。

对于parallelStream(),这是真的吗?@xyz:不要被流的使用这一事实所迷惑。只需看看代码结构。您有
a().b(c()).d().e()
,因此方法的调用顺序是
a
c
b
(接收
c
的结果),
d
e
a
方法是
stream
c
方法是
distinctByKey
b
方法是
filter
,等等,但实际的流操作只从
e
开始,即
reduce
。其他四个方法在此之前已经被调用并完成。@xyz同样适用。@Aominè确实,该方法只会被调用一次,但会有多个
谓词的实例returned@Aominè不用担心-当您想到只调用一次get的bootstrap方法时,它会变得更加有趣,一个
调用站点
,它将
方法句柄
包装到
get$Lambda
me通过
ASM
只链接一次的方法构建,每个流元素有多少不同谓词实例的细节是疯狂的?不,每次调用
distinctByKey
@StuartMarks都有一个CHM和一个谓词,调用它时@StuartMarks不会
获取$Lambda
始终返回一个新谓词?当然,但是
获取$Lambda()
只应在计算lambda表达式时调用,即从调用
distinctByKey
的内部调用。通过筛选器的每个流元素将调用同一lambda实例的
test()
方法。正是该实例保存捕获的CHM和keyExtractor函数。
Stream.of(new User("a"), new User("b"))
      .filter(distinctByKey(User::getName))
      .filter(distinctByKey(User::getName))
      .collect(Collectors.toList());
Predicate with hash :1259475182
Predicate with hash :1072591677
stream element : a
stream element : a
stream element : b
stream element : b