Limit a stream by a predicate

前端 未结 19 3109
别跟我提以往
别跟我提以往 2020-11-21 22:54

Is there a Java 8 stream operation that limits a (potentially infinite) Stream until the first element fails to match a predicate?

In Java 9 we can use

相关标签:
19条回答
  • 2020-11-21 23:44
        IntStream.iterate(1, n -> n + 1)
        .peek(System.out::println) //it will be executed 9 times
        .filter(n->n>=9)
        .findAny();
    

    instead of peak you can use mapToObj to return final object or message

        IntStream.iterate(1, n -> n + 1)
        .mapToObj(n->{   //it will be executed 9 times
                if(n<9)
                    return "";
                return "Loop repeats " + n + " times";});
        .filter(message->!message.isEmpty())
        .findAny()
        .ifPresent(System.out::println);
    
    0 讨论(0)
  • 2020-11-21 23:44

    Might be a bit off topic but this is what we have for List<T> rather than Stream<T>.

    First you need to have a take util method. This methods takes first n elements:

    static <T> List<T> take(List<T> l, int n) {
        if (n <= 0) {
            return newArrayList();
        } else {
            int takeTo = Math.min(Math.max(n, 0), l.size());
            return l.subList(0, takeTo);
        }
    }
    

    it just works like scala.List.take

        assertEquals(newArrayList(1, 2, 3), take(newArrayList(1, 2, 3, 4, 5), 3));
        assertEquals(newArrayList(1, 2, 3), take(newArrayList(1, 2, 3), 5));
    
        assertEquals(newArrayList(), take(newArrayList(1, 2, 3), -1));
        assertEquals(newArrayList(), take(newArrayList(1, 2, 3), 0));
    

    now it will be fairly simple to write a takeWhile method based on take

    static <T> List<T> takeWhile(List<T> l, Predicate<T> p) {
        return l.stream().
                filter(p.negate()).findFirst(). // find first element when p is false
                map(l::indexOf).        // find the index of that element
                map(i -> take(l, i)).   // take up to the index
                orElse(l);  // return full list if p is true for all elements
    }
    

    it works like this:

        assertEquals(newArrayList(1, 2, 3), takeWhile(newArrayList(1, 2, 3, 4, 3, 2, 1), i -> i < 4));
    

    this implementation iterate the list partially for a few times but it won't add add O(n^2) operations. Hope that's acceptable.

    0 讨论(0)
  • 2020-11-21 23:45

    Go to get library AbacusUtil. It provides the exact API you want and more:

    IntStream.iterate(1, n -> n + 1).takeWhile(n -> n < 10).forEach(System.out::println);
    

    Declaration: I'm the developer of AbacusUtil.

    0 讨论(0)
  • 2020-11-21 23:48

    Here is a version done on ints - as asked in the question.

    Usage:

    StreamUtil.takeWhile(IntStream.iterate(1, n -> n + 1), n -> n < 10);
    

    Here's code for StreamUtil:

    import java.util.PrimitiveIterator;
    import java.util.Spliterators;
    import java.util.function.IntConsumer;
    import java.util.function.IntPredicate;
    import java.util.stream.IntStream;
    import java.util.stream.StreamSupport;
    
    public class StreamUtil
    {
        public static IntStream takeWhile(IntStream stream, IntPredicate predicate)
        {
            return StreamSupport.intStream(new PredicateIntSpliterator(stream, predicate), false);
        }
    
        private static class PredicateIntSpliterator extends Spliterators.AbstractIntSpliterator
        {
            private final PrimitiveIterator.OfInt iterator;
            private final IntPredicate predicate;
    
            public PredicateIntSpliterator(IntStream stream, IntPredicate predicate)
            {
                super(Long.MAX_VALUE, IMMUTABLE);
                this.iterator = stream.iterator();
                this.predicate = predicate;
            }
    
            @Override
            public boolean tryAdvance(IntConsumer action)
            {
                if (iterator.hasNext()) {
                    int value = iterator.nextInt();
                    if (predicate.test(value)) {
                        action.accept(value);
                        return true;
                    }
                }
    
                return false;
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-21 23:49

    allMatch() is a short-circuiting function, so you can use it to stop processing. The main disadvantage is that you have to do your test twice: once to see if you should process it, and again to see whether to keep going.

    IntStream
        .iterate(1, n -> n + 1)
        .peek(n->{if (n<10) System.out.println(n);})
        .allMatch(n->n < 10);
    
    0 讨论(0)
  • 2020-11-21 23:50

    Operations takeWhile and dropWhile have been added to JDK 9. Your example code

    IntStream
        .iterate(1, n -> n + 1)
        .takeWhile(n -> n < 10)
        .forEach(System.out::println);
    

    will behave exactly as you expect it to when compiled and run under JDK 9.

    JDK 9 has been released. It is available for download here: JDK 9 Releases.

    0 讨论(0)
提交回复
热议问题