连续流操作是否有副作用?

时间:2016-08-04 19:33:01

标签: java java-stream 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()对并行流产生副作用会不安全。我的问题是,在顺序流上是否可以接受,就像在这种情况下一样?如果没有,为什么不呢?

2 个答案:

答案 0 :(得分:7)

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

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

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

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

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

一旦谓词返回false,allMatch将停止迭代。

答案 1 :(得分:-1)

这适合你吗?

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]

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