Non-terminal forEach() in a stream?

眉间皱痕 提交于 2019-12-05 13:18:13

问题


Sometimes when processing a Java stream() I find myself in need of a non-terminal forEach() to be used to trigger a side effect but without terminating processing.

I suspect I could do this with something like .map(item -> f(item)) where the method f performs the side effect and returns the item to the stream, but it seems a tad hokey.

Is there a standard way of handling this?


回答1:


Yes there is. It is called peek() (example from the JavaDoc):

Stream.of("one", "two", "three", "four")
     .peek(e -> System.out.println("Original value: " + e))
     .filter(e -> e.length() > 3)
     .peek(e -> System.out.println("Filtered value: " + e))
     .map(String::toUpperCase)
     .peek(e -> System.out.println("Mapped value: " + e))
     .collect(Collectors.toList());



回答2:


No, there is not.

peek() will only operate on all elements when forced to by a following operation. Can you predict what will be printed by this code?

public class Test
{
    private static final AtomicBoolean FLAG = new AtomicBoolean(false);

    private static void setFlagIfGreaterThanZero(int val)
    {
        if (val > 0) {
            FLAG.set(true);
        }
    }

    public static void main(String[] args)
    {
        FLAG.set(false);

        // Test 1
        IntStream.range(0, 10)
                 .peek(Test::setFlagIfGreaterThanZero)
                 .findFirst();

        System.out.println(FLAG.get());
        FLAG.set(false);

        // Test 2
        IntStream.range(0, 10)
                 .peek(Test::setFlagIfGreaterThanZero)
                 .sorted()
                 .findFirst();

        System.out.println(FLAG.get());
        FLAG.set(false);

        // Test 3
        IntStream.range(0, 10)
                 .boxed()
                 .peek(Test::setFlagIfGreaterThanZero)
                 .sorted()
                 .findFirst();

        System.out.println(FLAG.get());
        FLAG.set(false);

        // Test 4
        IntStream.range(0, 10)
                 .peek(Test::setFlagIfGreaterThanZero)
                 .filter(x -> x == 0)
                 .toArray();

        System.out.println(FLAG.get());
    }
}

The answer is:

false
false
true
true

That output might be intuitive if you have a solid understanding of Java Streams, but hopefully it also indicates that it's a very bad idea to rely on peek() as a mid-stream forEach().

map() also suffers the same issue. As far as I'm aware, there is no Stream operation that guarantees a sort of "process every element without taking shortcuts" behavior in every case independent of the prior and following operations.

Although this can be a pain, the short-circuiting behavior of Streams is an important feature. You might find this excellent answer to be useful: https://stackoverflow.com/a/32194320/507761



来源:https://stackoverflow.com/questions/40624971/non-terminal-foreach-in-a-stream

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!