我正在尝试将有限数量的值流式传输到集合中,但我需要在应用限制之前验证它们是否为新元素。例如:
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()
对并行流产生副作用会不安全。我的问题是,在顺序流上是否可以接受,就像在这种情况下一样?如果没有,为什么不呢?
答案 0 :(得分:7)
副作用和顺序流没有根本问题,但上面的第二个实现是无效的,因为流API不保证每个元素将依次在每个元素上执行
在第二个实现中,在应用限制之前,可以向destination
添加10个以上的元素。你的无操作forEach只会看到10,但你最终可能会得到更多。
除了流之外,java还有像for
和while
这样的循环结构,可以很容易地表达这样的内容。
如果你必须使用流,你可以这样做:
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个,它也可以工作。