如何在Java 8中修改并检查是否已修改?

4

我正在使用Java-8修改一个对象

users.stream().filter(u -> u.count > 0).forEach(u -> u.setProperty("value"))

然而,我想要知道是否修改了任何对象......即,我需要一个布尔返回值,但是这个函数只返回空。

有什么办法可以实现吗?


setProperty 返回什么?一个 boolean 吗? - Andrew Tobilko
为什么在调用setProperty后,对象不会被修改? - Andrew Tobilko
你认为什么时候算是修改了一个项目?当你调用setter方法的时候,还是当setter方法将其设置为之前没有的值时? - tobias_k
是的,设置属性被认为是已修改的。 - Tanvi Jaywant
3个回答

6

如果我理解正确,您想知道在执行操作时是否有任何匹配项。您可以简单地使用两个语句。

boolean anyMatch = users.stream().anyMatch(u -> u.count > 0);
if(anyMatch) users.stream().filter(u -> u.count > 0).forEach(u -> u.setProperty("value"));

由于anyMatch在第一次匹配到元素时就停止,所以只有在第一个匹配之前有大量不匹配的元素组成的长前缀时才会存在冗余工作。

如果这是一个问题,您可以使用

Spliterator<User> sp = users.stream().filter(u -> u.count > 0).spliterator();
boolean anyMatch = sp.tryAdvance(u -> u.setProperty("value"));
sp.forEachRemaining(u -> u.setProperty("value"));

替代方案。


2
读了问题 -> 嘿,这是一个完美的Spliterator案例 -> 写下来 -> 看了你的答案 :| -> 删除了我的 -> 给你的点赞 - Eugene

3

由于这是一个消耗性操作,它只能与不返回任何东西的方法一起使用;也就是说,使用forEach确保了终端操作,在这种操作中你不会得到返回值。

如果你想验证属性设置是否符合你的要求,你需要再次检查元素。

users.stream().filter(u -> u.count > 0)
              .allMatch(u -> u.getProperty().equals("value"));

虽然这更多的是针对偏执狂而言;除非 setProperty 有其他未暴露的副作用,否则 setter 应该 始终 设置值。 我会将上述内容编写为单元测试以进行验证,但不会在生产代码中使用。


不用再检查了,实际上你不必再检查 - Bohemian
3
最起码我会说,以这种方式检查最好还是留给单元测试,特别是考虑到 peek 实际上只是用于调试目的(参见https://dev59.com/XFwX5IYBdhLWcg3wlwWQ)。 - Makoto

2

添加一个调用 peek()

AtomicBoolean modified = new AtomicBoolean();
users.stream()
    .filter(u -> u.count > 0)
    .peek(u -> modified.set(true))
    .forEach(u -> u.setProperty("value"))

如果有任何元素通过了过滤器,modified.get() 将返回 true
使用 AtomicBoolean(或类似物)是必需的,因为在 lambda 中使用的引用必须是有效的 final

2
如果你使用peek的话,为什么不使用boolean modified = users.stream().filter(u -> u.count > 0).peek(u -> u.setProperty("value")).count() > 0;呢?这样就不需要用到AtomicBoolean。另一方面,如果你使用了AtomicBoolean,你就不需要使用peek了:users.stream().filter(u -> u.count > 0).forEach(u -> { modified.set(true); u.setProperty("value"); }); - Holger
@holger 因为添加 peek() 来设置标志不会改变 OP 现有的代码 - 它很容易添加和删除。它不会将修改检测嵌入到主代码中 - 参见关注点分离。每个流方法正在做什么以及为什么调用它是清晰的。在这种情况下,peek() 是正确的方法。 - Bohemian
3
如果删除终端操作符,peek内的操作将停止工作,因此不存在真正的“责任分离”,而是一个不太明显的依赖关系。 - Holger
@holger,如果您删除终端操作,那么无法使用peek,这是很好的,因为OP想要计算的是终端操作。实际上,关注点是分离的:使用peek,您可以在不触及或影响任何其他代码的情况下删除计数。实际上,像我这样格式化后,您可以通过注释掉peek行从流中删除它-多简单啊! - Bohemian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接