Java 8 Stream, getting head and tail

后端 未结 9 2047
太阳男子
太阳男子 2020-11-30 05:40

Java 8 introduced a Stream class that resembles Scala\'s Stream, a powerful lazy construct using which it is possible to do something like this very concisely:



        
相关标签:
9条回答
  • 2020-11-30 06:35

    There are many interesting suggestions provided here, but if someone needs a solution without dependencies to third party libraries I came up with this:

        import java.util.AbstractMap;
        import java.util.Optional;
        import java.util.Spliterators;
        import java.util.stream.StreamSupport;
    
        /**
         * Splits a stream in the head element and a tail stream.
         * Parallel streams are not supported.
         * 
         * @param stream Stream to split.
         * @param <T> Type of the input stream.
         * @return A map entry where {@link Map.Entry#getKey()} contains an
         *    optional with the first element (head) of the original stream
         *    and {@link Map.Entry#getValue()} the tail of the original stream.
         * @throws IllegalArgumentException for parallel streams.
         */
        public static <T> Map.Entry<Optional<T>, Stream<T>> headAndTail(final Stream<T> stream) {
            if (stream.isParallel()) {
                throw new IllegalArgumentException("parallel streams are not supported");
            }
            final Iterator<T> iterator = stream.iterator();
            return new AbstractMap.SimpleImmutableEntry<>(
                    iterator.hasNext() ? Optional.of(iterator.next()) : Optional.empty(),
                    StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false)
            );
        }
    
    0 讨论(0)
  • 2020-11-30 06:39

    The solution below does not do state mutations, except for the head/tail deconstruction of the stream.

    The lazyness is obtained using IntStream.iterate. The class Prime is used to keep the generator state

        import java.util.PrimitiveIterator;
        import java.util.stream.IntStream;
        import java.util.stream.Stream;
    
        public class Prime {
            private final IntStream candidates;
            private final int current;
    
            private Prime(int current, IntStream candidates)
            {
                this.current = current;
                this.candidates = candidates;
            }
    
            private Prime next()
            {
                PrimitiveIterator.OfInt it = candidates.filter(n -> n % current != 0).iterator();
    
                int head = it.next();
                IntStream tail = IntStream.generate(it::next);
    
                return new Prime(head, tail);
            }
    
            public static Stream<Integer> stream() {
                IntStream possiblePrimes = IntStream.iterate(3, i -> i + 1);
    
                return Stream.iterate(new Prime(2, possiblePrimes), Prime::next)
                             .map(p -> p.current);
            }
        }
    

    The usage would be this:

    Stream<Integer> first10Primes = Prime.stream().limit(10)
    
    0 讨论(0)
  • 2020-11-30 06:39

    To get head and tail you need a Lazy Stream implementation. Java 8 stream or RxJava are not suitable.

    You can use for example LazySeq as follows.

    Lazy sequence is always traversed from the beginning using very cheap first/rest decomposition (head() and tail())

    LazySeq implements java.util.List interface, thus can be used in variety of places. Moreover it also implements Java 8 enhancements to collections, namely streams and collectors


    package com.company;
    
    import com.nurkiewicz.lazyseq.LazySeq;
    
    public class Main {
    
        public static void main(String[] args) {
    
            LazySeq<Integer> ints = integers(2);
            LazySeq primes = sieve(ints);
            primes.take(10).forEach(p -> System.out.println(p));
    
        }
    
        private static LazySeq<Integer> sieve(LazySeq<Integer> s) {
            return LazySeq.cons(s.head(), () -> sieve(s.filter(x -> x % s.head() != 0)));
        }
    
        private static LazySeq<Integer> integers(int from) {
            return LazySeq.cons(from, () -> integers(from + 1));
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题