【问题标题】:Mutable parameters in Java 8 StreamsJava 8 Streams 中的可变参数
【发布时间】:2014-05-19 17:04:10
【问题描述】:

看这个问题:How to dynamically do filtering in Java 8?

问题是在执行过滤器后截断流。我不能使用limit,因为我不知道过滤器之后的列表有多长。那么,我们可以计算过滤后的元素吗?

所以,我想我可以创建一个类来计数并通过映射传递流。The code is in this answer

我创建了一个计数但保持元素不变的类,我在这里使用了一个函数,以避免使用我在另一个答案中使用的 lambda:

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().

如果我删除该行,结果是当我尝试执行限制时计数器为零。我通过使用可变对象以某种方式欺骗了“有效最终”的要求。

我可能错了,但我理解流是首先构建的,所以如果我们使用可变对象将参数传递给流中的任何步骤,这些将在创建流时进行。

我的问题是,如果我的假设是正确的,为什么需要这样做?流(如果非并行)可以按顺序通过所有步骤(过滤器、映射..),因此不需要此限制。

【问题讨论】:

    标签: java java-8 java-stream


    【解决方案1】:

    简答

    我的问题是,如果我的假设是正确的,为什么需要这样做?这 流(如果非并行)可以按顺序通过所有 步骤(过滤器,地图..),因此不需要此限制。

    如您所知,对于 并行 流,这听起来很明显:需要此限制,否则结果将是不确定的。

    关于非并行流,由于它们当前的设计,这是不可能的:每个项目只被访问一次。如果流确实按照您的建议工作,他们会在进入下一步之前对整个集合执行每个步骤,我认为这可能会对性能产生影响。我怀疑这就是语言设计者做出这个决定的原因。


    为什么没有collect 它在技术上不起作用

    你已经知道了,但这里是给其他读者的解释。 来自the docs

    流是惰性的;仅对源数据进行计算 当终端操作启动时,源元素是 仅在需要时消耗。

    Stream 的每个中间操作,例如filter()limit() 实际上只是某种初始化流选项的设置器。

    当您调用终端操作时,例如forEach()collect()count(),这就是计算发生的时候,按照先前构建的管道处理项目。

    这就是为什么 limit() 的参数在单个项目通过流的第一步之前进行评估的原因。这就是为什么您需要使用终端操作结束流,然后使用 limit() 开始一个新的流,然后您就会知道。

    关于为什么不允许并行流的更详细答案

    让您的流管道为step X &gt; step Y &gt; step Z

    我们希望对我们的项目进行并行处理。因此,如果我们允许步骤 Y 的行为依赖于已经通过 X 的项目,那么 Y 是不确定的。这是因为在一个项目到达步骤 Y 的那一刻,已经通过 X 的项目集在多次执行中将不一样(因为线程)。

    关于为什么不允许它用于非并行流的更详细的答案

    根据定义,流用于处理中的项目。您可以将非并行流视为如下:一个项目经历所有步骤,然后下一个项目经历所有步骤,等等。事实上,文档说明了一切:

    流的元素在其生命周期内只被访问一次 溪流。像迭代器一样,必须生成一个新流才能重新访问 源的相同元素。

    如果流不是这样工作的,那么在进入下一步之前,只对整个集合执行每个步骤就再好不过了。这实际上允许在非并行流中使用可变参数,但它可能会对性能产生影响(因为我们会在集合上进行多次迭代)。无论如何,他们目前的行为不允许你想要的。

    【讨论】:

    • 你说:“如果流程中的一个步骤需要知道前面步骤中所有项目的处理结果,那么整个流的想法就行不通了。”。为什么?在链接的问题中,用户希望在应用过滤器后获得前 50%(因此我们不能使用限制,因为我们不知道会有多少)。为什么你认为它违背了流的概念。
    • 因为,恕我直言,使用流意味着您希望以相同的方式处理每个项目(例如在汽车工厂:在当前汽车上添加东西,然后将其传递到下一步)。在这里,如果您需要根据所有项目知道一个结果,那么每个项目的处理是不一样的。这就是需要终端操作的原因。
    • 听起来不错。但是我们还是有filter和limit的,经过filter和limit后有些item不会被处理
    • 好吧filter过滤所有项目的方式相同,它不需要提前知道所有项目,只需要知道当前的样子。
    • 至于limit,它不依赖于未来,只需要记住过去的项目。
    猜你喜欢
    • 2015-11-19
    • 1970-01-01
    • 2020-06-27
    • 1970-01-01
    • 2019-02-25
    • 2020-03-21
    • 1970-01-01
    • 2015-06-19
    • 1970-01-01
    相关资源
    最近更新 更多