【问题标题】:The use of .peek() in java 8 streams在 java 8 流中使用 .peek()
【发布时间】:2017-06-26 11:51:26
【问题描述】:

我在我的信息流中使用 .peek() 但它不受欢迎,遗憾的是我找不到解决方案。

简化版:

static boolean fooAddTester(int size) {
    Foo foo = new foo(); //data structure

    return IntStream.range(0, size).
            .peek(i -> synchronized(r){foo.add(i)})
            .allMatch(e -> foo.isLegal());
}

我需要做的是遍历 IntStream 并检查 在每次插入后 foo 数据结构是否合法。 这在逻辑上等价于:

static boolean fooAddTester(int size) {
    Foo foo = new foo(); //data structure

    for(int i=0; i<size; i++){
        foo.add(i);
        if(!foo.isLegal())
            return false;
    return true;
}

但是它更复杂,我正在尝试使用流来简化和学习。

在不使用.peek() 的情况下做同样的事情的方法是:这确实有效 - 但我只是将问题“转移”到.allMatch()

return IntStream.range(0, size).
            .allMatch(i -> {
                 synchronized(r){foo.add(i)};
                 foo.isLegal();
             )};

我的问题与this question 非常相似,不同之处在于我每次都在检查,因此解决方案不起作用。

所以我的问题是:

  • .peek() 真的只用于调试还是我可以以这种方式使用它?
  • 有没有更好的解决方案?
  • 我应该使用第二种解决方案吗?

我正在寻找一个正确的解决方案,而不是一个有效的解决方案,所有这些代码都已经在工作了。

【问题讨论】:

  • 你要检查一个条件after every insert;这依赖于副作用,应该避免。
  • synchronized 不会对您有太大帮助 - 它会保证一次只将一个元素放入foo;但它不能保证 哪个 元素被放入 foo,因为在并行流的情况下没有处理顺序
  • “新手”的好问题
  • 什么是r?你为什么要同步它?您的流代码与循环不匹配。您正在流式传输int 值,因此您需要static 方法Foo.isLegal(int) 以使编译器接受第一个sn-p。在最后一个 sn-p 中,您在不允许方法引用的地方使用方法引用 Foo::isLegal。显然,您从未尝试过任何这些替代方案。

标签: java java-8 java-stream


【解决方案1】:

Stream#peek 的文档已经提到如下,主要不是绝对

此方法的存在主要是为了支持调试,您希望在元素流过管道中的某个点时查看它们

@Holger绝对回答了这个问题:

您可以用peek 做的有用的事情是找出流元素是否已被处理。

他在回答中也指出了一些副作用,peek 操作取决于调用了哪个终端操作。所以在内部使用peek 时要小心。

所以正确的方法是使用for-each 循环,因为Stream#collect 不支持short-circuiting operation

可选方式是使用peek,因为您可以自己控制流。并且您需要删除 synchornized 块,这里没有必要。

return IntStream.range(0, size).peek(foo::add).allMatch(__ -> Foo.isLegal(foo));

【讨论】:

  • 好吧the most useful thing you can do with peek is to find out whether a stream element has been processed.,但是通过记录,而不是依赖副作用。一旦您添加parallel,这将以神秘的方式中断。 synchronized 将确保一次只处理一个元素,但即使那样你也不知道哪个元素,有 处理顺序
  • @Eugene 但我想说 OP 可以控制如何创建、使用和操作流。所以这里没有问题。
  • 我添加了synchornized,最初是为了使用parallel()。非常感谢您的回答!
【解决方案2】:

如果你真的想在你的逻辑之前使用其他流操作,我只能想到一种方法,但我并不是它的忠实粉丝......

 boolean result = true;
    try {
        IntStream.range(0, 10)
                .forEachOrdered(x -> {
                    foo.add(x);
                    if (!Foo.isLegal(foo)) {
                        throw new RuntimeException("just because");
                    }
                });
    } catch (RuntimeException re) {
        result = false;
    }

    System.out.println(result);

显然,您需要将RuntimeException 替换为您的某种异常。

【讨论】:

    猜你喜欢
    • 2017-05-23
    • 2021-01-20
    • 2019-06-04
    • 2016-02-11
    • 1970-01-01
    • 2015-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多