看看这个问题:How to dynamically do filtering in Java 8?
问题是在执行过滤器后截断流。我不能使用limit因为我不知道列表在过滤器之后有多长。那么,我们可以计算过滤器之后的数据吗?
所以,我想我可以创建一个计算并通过地图传递流的类。The code is in this answer。
我创建了一个重要的类但保持元素不变,我在这里使用了一个函数,以避免使用我在另一个答案中使用的lambdas:
class DoNothingButCount<T > implements Function<T, T> {
AtomicInteger i;
public DoNothingButCount() {
i = new AtomicInteger(0);
}
public T apply(T p) {
i.incrementAndGet();
return p;
}
}
所以我的Stream终于来了:
persons.stream()
.filter(u -> u.size > 12)
.filter(u -> u.weitght > 12)
.map(counter)
.sorted((p1, p2) -> p1.age - p2.age)
.collect(Collectors.toList())
.stream()
.limit((int) (counter.i.intValue() * 0.5))
.sorted((p1, p2) -> p2.length - p1.length)
.limit((int) (counter.i.intValue() * 0.5 * 0.2)).forEach((p) -> System.out.println(p));
但我的问题是关于我的例子的另一部分。
collect(Collectors.toList()).stream().
如果我删除该行,结果是当我尝试执行限制时计数器为零。我在某种程度上欺骗了#34; efectively final&#34;通过使用可变对象的要求。
我可能错了,但我理解流是首先构建的,所以如果我们使用可变对象将参数传递给流中的任何步骤,则在创建流时将采用这些步骤。
我的问题是,如果我的假设是正确的,为什么需要这样做?流(如果非并行)可以顺序通过所有步骤(过滤器,映射..),因此不需要此限制。
答案 0 :(得分:8)
我的问题是,如果我的假设是正确的,为什么需要这样做?该 流(如果非并行)可以顺序通过所有 步骤(过滤器,地图..)所以不需要这个限制。
正如您所知,对于并行流,这听起来非常明显:需要此限制,否则结果将是不确定的。
关于非并行流,因为它们当前的设计是不可能的:每个项目只访问一次。如果溪流确实按照您的建议工作,他们会在进入下一步之前对整个集合执行每一步,我认为这可能会对性能产生影响。我怀疑这就是语言设计师做出这个决定的原因。
collect
你已经知道了,但这是其他读者的解释。 来自the docs:
流是懒惰的;仅对源数据进行计算 当启动终端操作时,源元素是 只在需要时消费。
Stream
的每个中间操作,例如filter()
或limit()
实际上只是某种初始化流选项的setter。
当您调用终端操作,例如forEach()
,collect()
或count()
时,就会发生计算,处理之前构建的管道之后的项目
这就是在单个项目完成流的第一步之前评估limit()
的参数的原因。这就是为什么你需要用终端操作结束流,然后用你知道的limit()
开始一个新的。
让您的流管道为step X > step Y > step Z
。
我们想要平行处理我们的物品。因此,如果我们允许步骤Y的行为依赖于已经通过X的项目,那么Y是非确定性的。这是因为当项目到达步骤Y时,已经通过X的项目集在多次执行中将是不同的(因为线程)。
根据定义,流用于处理流中的项目。你可以想到一个非并行流如下:一个单项完成所有步骤,然后下一个完成所有步骤,等等。实际上,文档说明了一切:
流的元素仅在a的生命期间访问过一次 流。像Iterator一样,必须生成一个新的流来重新访问 源的相同元素。
如果stream不能像这样工作,那么在进行下一步之前,只需在整个集合中执行每一步就没有任何好处。 实际上允许非并行流中的可变参数,但它可能会对性能产生影响(因为我们会在集合上多次迭代)。无论如何,他们目前的行为不允许你想要的东西。