顺序流操作会产生副作用吗?

Can sequential stream operations have side-effects?

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

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

但是多余的 contains() 检查让我很困扰,因为 add() 可以同时添加元素 报告它是否是集合中的新元素。所以我想这样做:

source.stream()
        .filter(destination::add)
        .limit(10)
        .forEach(i -> {});    // no-op terminal operation to force evaluation

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

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

在第二个实现中,在应用限制之前可以向 destination 添加 10 个以上的元素。您的 no-op forEach 只会看到 10 个,但您最终可能会得到更多。

除了流之外,java 有像 forwhile 这样的循环结构,可以让表达这样的事情变得容易。

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

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

一旦谓词returns为假,allMatch就会停止迭代。

这对你有用吗:

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);
    }

}

运行 这个 class 将打印:

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]

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