Predicate如何在Java 8中维护状态

Foo*_*Bar 6 java-8 java-stream

我正在查看此代码并尝试理解以下代码.

复制自Stuart Marks的回答

public static <T> Predicate<T> distinctByKey(Function<? super T,Object> keyExtractor) {
    Map<Object,Boolean> seen = new ConcurrentHashMap<>();
    return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}

BigDecimal totalShare = orders.stream()
    .filter(distinctByKey(o -> o.getCompany().getId()))
    .map(Order::getShare)
    .reduce(BigDecimal.ZERO, BigDecimal::add);
Run Code Online (Sandbox Code Playgroud)

我的问题是每次调用distinctByKey并生成新的ConcurrentHashMap.如何使用新的ConcurrentHashMap <>()来维护状态;

Eug*_*ene 6

由于这是一个捕获的λ,确实是一个新的Predicate实例将被返回的所有每次通话的时间-但这些实例将共享相同的ConcurrentHashMapFunction.

顺便说一下,添加一个System.out.println内部 distinctByKey- 并看到它实际上只被调用一次.

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

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

你会看到class为你的实现生成了一个Predicate.因为这是一个有状态的lambda - 它捕获了CHMFunction,它将有一个private构造函数和一个static factory method返回一个实例.因此,每一个元素穿过流管道时会出现一个不同的实例Predicate创建的-但所有这些实例将共享相同的ConcurrentHashMapFunction.


Ous*_* D. 6

这看起来很混乱,但它很简单。实际发生的情况是该distinctByKey方法被调用一次,因此永远只有一个 的实例,ConcurrentHashMap并且它被 lambda 表达式捕获。因此,当该distinctByKey方法返回一个Predicate对象时,我们会将其应用于流的每个元素。

  • @xyz:不要因为有流使用而感到困惑。只需看看*代码结构*。你有`a().b(c()).d().e()`,所以这些方法按`a`、`c`、`b`的顺序调用(接收`c`的结果)、`d`、`e`。`a` 方法是 `stream`,`c` 是 `distinctByKey`,`b` 是 `filter` 等,但实际的流操作只是从 `e` 开始,也就是 `reduce`。其他四个方法在此之前已经调用完成。 (4认同)
  • @Aominè 不用担心 - 当你想到只被调用一次的引导方法时,它会变得更有趣,一个 `CallSite` 将 `MethodHandle` 包装到由 `ASM` 构建的 `get$Lambda` 方法只链接一次,它是疯狂有多少细节 (2认同)