Java 连续流操作会有副作用吗?

Java 连续流操作会有副作用吗?,java,java-stream,side-effects,Java,Java Stream,Side Effects,我试图将有限数量的值流式传输到一个集合中,但在应用限制之前,我需要验证它们是否为新元素。例如: Set<Integer> destination = ... Set<Integer> source = ... source.stream() .filter(i -> !destination.contains(i)) .limit(10) .forEach(destination::add); 忽略黑客终端操作,存

我试图将有限数量的值流式传输到一个集合中,但在应用限制之前,我需要验证它们是否为新元素。例如:

Set<Integer> destination = ... 
Set<Integer> source = ...
source.stream()
        .filter(i -> !destination.contains(i))
        .limit(10)
        .forEach(destination::add);

忽略黑客终端操作,存在使用带有副作用的过滤操作的问题,这通常是不鼓励的。我理解为什么使用
map()
filter()
对并行流产生副作用是不安全的。我的问题是,像在这种情况下,在顺序流上是否可以接受?如果没有,为什么不呢?

副作用和顺序流没有根本问题,但是上面的第二个实现是无效的,因为流API不能保证每个阶段将依次在每个元素上执行

在第二种实现中,在应用限制之前,可以向
目的地
添加10个以上的元素。你的无操作forEach只能看到10个,但最终你可能会在集合中看到更多

除了流之外,java还有循环结构,如
for
while
,可以很容易地表达这样的内容

如果必须使用streams,可以这样做:

int maxSize = destination.size()+10;
source.stream().allMatch(x -> destination.size()<maxsize && (destination.add(x)||true));
int maxSize=destination.size()+10;

source.stream().allMatch(x->destination.size()这是否适用于您:

package be.objectsmith;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Playground {
    public static void main(String[] args) {
        copy(
            IntStream.range(1, 20).boxed().collect(Collectors.toSet()),
            new HashSet<>(Arrays.asList(2, 5)));
        copy(
            IntStream.range(1, 5).boxed().collect(Collectors.toSet()),
            new HashSet<>(Arrays.asList(2, 5)));
    }

    private static void copy(Set<Integer> source, Set<Integer> destination) {
        source
            .stream()
            .map(destination::add)
            .filter(resultOfAdding -> resultOfAdding)
            .limit(10)
            .collect(Collectors.toList());  // Need a terminal operation
        System.out.println("source = " + source);
        System.out.println("destination = " + destination);
    }

}
如您所见,它只添加了10个元素。
第二个调用表明,如果要添加的元素少于10个,它也可以工作。

这只是问题中第二个实现的更复杂版本,分解过滤器(destination::add)分为两个操作。@MattTimmermans这是对第二个实现的修复,它完全满足OP的要求:利用
Set.add()
本身报告它是否是新元素的事实。然后它只需要计数并限制成功的添加。我甚至会用
.count()替换终端操作
。总的来说,我还是更喜欢第一个实现;
add
报告成功是一种方便,它允许更优雅地表达一些案例,但在这里我只看到添加的inconvenience@bowmore它修复了什么?原始版本也适用于给定的测试用例,使用Set::add的返回,并且具有o此答案中不包含的问题。@MattTimmermans再次查看了原始版本,您是对的。我认为您对我的场景有点误解。
source
是一个集合,因此它不包含重复的元素。我试图确保我们添加的元素来自
source
,在
目标中不存在。因此,我认为第一种解决方案仍然有效。但您的总体观点是一个有趣的问题,即流元素可能不会一次通过整个管道。您是否有这方面的源代码,或者您是否依赖于缺少相反的文档?缺少相反的文档,以及大量optim流上的优化涉及到成批处理元素。您编写的代码今天似乎可以工作,但它依赖于新Java版本或其他实现中可能发生的变化。我确实误解了检查的目的。我编辑了相应的答案,事实上,答案明确表示某些有状态操作像
sorted()
这样的定量必须同时应用于多个元素,我想说一些实现可能会选择对无状态操作也这样做也不算太长。除非我找到相反的证据,否则接受这个答案。如果你想举个例子,这失败的地方:with
Stream.of(source).flatMap(Set::stream).filter(destination::add).limit(10).forEach(i->{});
,该限制无效。(当然,
forEach
的操作最多被正确调用十次)
package be.objectsmith;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Playground {
    public static void main(String[] args) {
        copy(
            IntStream.range(1, 20).boxed().collect(Collectors.toSet()),
            new HashSet<>(Arrays.asList(2, 5)));
        copy(
            IntStream.range(1, 5).boxed().collect(Collectors.toSet()),
            new HashSet<>(Arrays.asList(2, 5)));
    }

    private static void copy(Set<Integer> source, Set<Integer> destination) {
        source
            .stream()
            .map(destination::add)
            .filter(resultOfAdding -> resultOfAdding)
            .limit(10)
            .collect(Collectors.toList());  // Need a terminal operation
        System.out.println("source = " + source);
        System.out.println("destination = " + destination);
    }

}
source = [1, 2, 3, 4]
destination = [1, 2, 3, 4, 5]
source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
destination = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]