복붙노트

[SCALA] 자바 8 스트림, 점점 머리와 꼬리

SCALA

자바 8 스트림, 점점 머리와 꼬리

자바 8 스칼라의 스트림, 매우 간결 같은 것을 할 수 있습니다 사용하여 강력한 게으른 구조와 유사한 스트림 클래스를 소개 :

def from(n: Int): Stream[Int] = n #:: from(n+1)

def sieve(s: Stream[Int]): Stream[Int] = {
  s.head #:: sieve(s.tail filter (_ % s.head != 0))
}

val primes = sieve(from(2))

primes takeWhile(_ < 1000) print  // prints all primes less than 1000

이 자바 8에서이 작업을 수행 할 수 있다면이 같은 것을 쓴, 그래서 내가 궁금 :

IntStream from(int n) {
    return IntStream.iterate(n, m -> m + 1);
}

IntStream sieve(IntStream s) {
    int head = s.findFirst().getAsInt();
    return IntStream.concat(IntStream.of(head), sieve(s.skip(1).filter(n -> n % head != 0)));
}

IntStream primes = sieve(from(2));

매우 간단하지만 java.lang.IllegalStateException가 생성 () 스트림이 이미 동작시 또는 둘다로 findFirst 때문에 () 및 폐쇄 이동 한 번만 수행 될 수 스트림에서 단말기 동작이다.

나는 정말로 내가 필요 스칼라의 Stream.head 및 Stream.tail의 해당 스트림의 첫 번째 숫자와 다른 스트림으로 나머지, 즉 때문에 두 번 스트림을 사용할 필요가 없습니다. 나는 이것을 달성하는 데 사용할 수있는 자바 (8) 스트림의 방법이 있습니까?

감사.

해결법

  1. ==============================

    1.당신은 당신이 IntStream을 분할 할 수 없다는 문제가없는 한 경우에도 재귀 대신 느리게 당신의 체 메소드를 호출하기 때문에, 당신의 코드는 일을하지 않았다. 첫 번째 값에 대한 결과의 스트림을 조회하기 전에 그래서 당신은 무한 재귀했다.

    당신은 당신이 IntStream을 분할 할 수 없다는 문제가없는 한 경우에도 재귀 대신 느리게 당신의 체 메소드를 호출하기 때문에, 당신의 코드는 일을하지 않았다. 첫 번째 값에 대한 결과의 스트림을 조회하기 전에 그래서 당신은 무한 재귀했다.

    IntStream는 머리와 꼬리 IntStream (아직 소비되지 않은)로 변환하는 분할이 가능하다 :

    PrimitiveIterator.OfInt it = s.iterator();
    int head = it.nextInt();
    IntStream tail = IntStream.generate(it::next).filter(i -> i % head != 0);
    

    이 곳에서 당신은 느리게 꼬리에 ​​체를 호출하는 구조가 필요합니다. 스트림은 제공하지 않습니다; CONCAT 인수로 스트림 인스턴스를 기존 예상하는 게으른 창조 람다 표현식을 지원하지 않는 경우에만 변경 가능한 상태로 작동하기 때문에 당신은 람다 식으로 체 유유히 호출 스트림을 구성 할 수 없습니다. 당신이 라이브러리 구현이 변경 가능한 상태를 숨기고이없는 경우에는 변경 가능한 객체를 사용합니다. 당신이 변경 가능한 상태의 요구 사항을 수용하지만 일단이 솔루션은 첫 번째 방법보다 쉽게 ​​할 수 있습니다 :

    IntStream primes = from(2).filter(i -> p.test(i)).peek(i -> p = p.and(v -> v % i != 0));
    
    IntPredicate p = x -> true;
    
    IntStream from(int n)
    {
      return IntStream.iterate(n, m -> m + 1);
    }
    

    이 재귀 적 필터를 만들하지만 (이 작업을 한 경우 IntStream.concat 접근 방식처럼) 결국이 문제는 IntPredicates의 나무 또는 IntStreams의 트리를 만들지 않습니다 여부를 지정합니다. 당신은 필터의 가변 인스턴스 필드 마음에 들지 않으면 당신은 (...하지만 람다 식의) 내부 클래스에서 숨길 수 있습니다.

  2. ==============================

    2.이 솔루션은 아래 스트림의 헤드 / 테일 해체를 제외하고, 상태 변이를하지 않습니다.

    이 솔루션은 아래 스트림의 헤드 / 테일 해체를 제외하고, 상태 변이를하지 않습니다.

    게으름은 IntStream.iterate를 사용하여 얻어진다. 클래스 총리는 발전기 상태를 유지하는 데 사용됩니다

        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);
            }
        }
    

    사용법이 될 것입니다 :

    Stream<Integer> first10Primes = Prime.stream().limit(10)
    
  3. ==============================

    3.내 StreamEx 라이브러리는 문제를 해결 지금 headTail () 작업이 있습니다 :

    내 StreamEx 라이브러리는 문제를 해결 지금 headTail () 작업이 있습니다 :

    public static StreamEx<Integer> sieve(StreamEx<Integer> input) {
        return input.headTail((head, tail) -> 
            sieve(tail.filter(n -> n % head != 0)).prepend(head));
    }
    

    headTail 방법은 일단 스트림 단말기 동작 실행 중에 많아야 실행될 것이다 BiFunction 걸린다. 이 탐색이 시작 될 때까지 계산 아무것도하지 않습니다 및 요청에 따라 단지 많은 소수로 계산 :이 구현은 게으른 그래서. BiFunction은 제 스트림 요소 머리와 나머지 요소 꼬리의 스트림을 수신하고 원하는 어떤 방법으로 꼬리를 수정할 수 있습니다. 미리 정의 된 입력으로 사용할 수 있습니다 :

    sieve(IntStreamEx.range(2, 1000).boxed()).forEach(System.out::println);
    

    그러나 무한 스트림 작업뿐만 아니라

    sieve(StreamEx.iterate(2, x -> x+1)).takeWhile(x -> x < 1000)
         .forEach(System.out::println);
    // Not the primes till 1000, but 1000 first primes
    sieve(StreamEx.iterate(2, x -> x+1)).limit(1000).forEach(System.out::println);
    

    headTail와 술어 연결을 사용하여 대체 솔루션도 있습니다 :

    public static StreamEx<Integer> sieve(StreamEx<Integer> input, IntPredicate isPrime) {
        return input.headTail((head, tail) -> isPrime.test(head) 
                ? sieve(tail, isPrime.and(n -> n % head != 0)).prepend(head)
                : sieve(tail, isPrime));
    }
    
    sieve(StreamEx.iterate(2, x -> x+1), i -> true).limit(1000).forEach(System.out::println);
    

    그것은 흥미로운 재귀 솔루션을 비교 : 그들이 할 수있는 생성하는 방법을 많은 소수.

    소매점을 @ 존 용액 (StreamUtils)

    존 서 소매점 솔루션은 게으른하지 않은 : 당신은 무한 스트림을 공급되지 않습니다. 난 그냥 시행 착오에 의해 발견 그래서 상 허용되는 최대 바운드 (17793) (StackOverflowError가 발생 그 후) :

    public void sieveTest(){
        sieve(IntStream.range(2, 17793).boxed()).forEach(System.out::println);
    }
    

    소매점을 @ 존 용액 (Streamable의)

    public void sieveTest2(){
        sieve(Streamable.range(2, 39990)).forEach(System.out::println);
    }
    

    StackOverflowError가에서 39,990 결과 위의 상한을 증가.

    @frhack 용액 (LazySeq)

    LazySeq<Integer> ints = integers(2);
    LazySeq primes = sieve(ints); // sieve method from @frhack answer
    primes.forEach(p -> System.out.println(p));
    

    결과 : 엄청난 힙 할당 및 가비지 컬렉션이 90 % 이상을 복용 소수 = 53327 후 붙어. 더 기다리는 것은 비현실적 보인다 그래서는 53,323에서 53,327에 사전에 몇 분했다.

    @ 신 솔루션

    Prime.stream().forEach(System.out::println);
    

    결과 : StackOverflowError가를 소수 = 134417 후.

    내 솔루션 (StreamEx)

    sieve(StreamEx.iterate(2, x -> x+1)).forEach(System.out::println);
    

    결과 : StackOverflowError가를 소수 = 236167 후.

    @frhack 용액 (rxjava)

    Observable<Integer> primes = Observable.from(()->primesStream.iterator());
    primes.forEach((x) -> System.out.println(x.toString()));            
    

    결과 : StackOverflowError가를 소수 = 367663 후.

    @Holger 솔루션

    IntStream primes=from(2).filter(i->p.test(i)).peek(i->p=p.and(v->v%i!=0));
    primes.forEach(System.out::println);
    

    결과 : StackOverflowError가를 소수 = 368089 후.

    내 솔루션 (술어 연결과 StreamEx)

    sieve(StreamEx.iterate(2, x -> x+1), i -> true).forEach(System.out::println);
    

    결과 : StackOverflowError가를 소수 = 368287 후.

    각각의 새로운 조건은 2 개 스택 프레임을 추가하기 때문에, 술어 연결 승리를 포함하는 세 가지 솔루션 그래서. 나는 그들 사이의 차이가 한계이며 승자를 정의하는 것으로 간주되어서는 안된다 생각합니다. 그러나 나는 처음 StreamEx 솔루션 같은 더으로 스칼라 코드와 더 유사합니다.

  4. ==============================

    4.당신은 기본적으로 다음과 같이 그것을 구현할 수 있습니다 :

    당신은 기본적으로 다음과 같이 그것을 구현할 수 있습니다 :

    static <T> Tuple2<Optional<T>, Seq<T>> splitAtHead(Stream<T> stream) {
        Iterator<T> it = stream.iterator();
        return tuple(it.hasNext() ? Optional.of(it.next()) : Optional.empty(), seq(it));
    }
    

    위의 예에서, Tuple2 서열은 jOOλ, 우리가 jOOQ 통합 테스트를 위해 개발 한 도서관에서 빌린 종류가 있습니다. 당신이 추가 종속성을 원하지 않는 경우, 당신은뿐만 아니라 그들에게 자신을 구현할 수 :

    class Tuple2<T1, T2> {
        final T1 v1;
        final T2 v2;
    
        Tuple2(T1 v1, T2 v2) {
            this.v1 = v1;
            this.v2 = v2;
        }
    
        static <T1, T2> Tuple2<T1, T2> tuple(T1 v1, T2 v2) {
            return new Tuple<>(v1, v2);
        }
    }
    
    static <T> Tuple2<Optional<T>, Stream<T>> splitAtHead(Stream<T> stream) {
        Iterator<T> it = stream.iterator();
        return tuple(
            it.hasNext() ? Optional.of(it.next()) : Optional.empty,
            StreamSupport.stream(Spliterators.spliteratorUnknownSize(
                it, Spliterator.ORDERED
            ), false)
        );
    }
    
  5. ==============================

    5.당신이 제 3의 라이브러리의 외눈 박이 스트림을 사용하여 괜찮다면, 나는 도서관 나는 잠재적 인 솔루션을 가지고 있습니다 썼다.

    당신이 제 3의 라이브러리의 외눈 박이 스트림을 사용하여 괜찮다면, 나는 도서관 나는 잠재적 인 솔루션을 가지고 있습니다 썼다.

    StreamUtils 클래스는 headAndTail 포함 java.util.stream.Streams 직접 작업을위한 정적 메소드의 많은 수 있습니다.

    HeadAndTail<Integer> headAndTail = StreamUtils.headAndTail(Stream.of(1,2,3,4));
    int head = headAndTail.head(); //1
    Stream<Integer> tail = headAndTail.tail(); //Stream[2,3,4]
    

    Streamable의 클래스는 replayable 스트림을 나타내는 중간 데이터 구조를 캐싱, 게으른을 구축하여 작동합니다. 이 캐시되어 있기 때문에 상환 - 머리와 꼬리는 직접 개별적으로 구현 될 수있다.

    Streamable<Integer> replayable=  Streamable.fromStream(Stream.of(1,2,3,4));
    int head = repayable.head(); //1
    Stream<Integer> tail = replayable.tail(); //Stream[2,3,4]
    

    사이클롭스 스트림도 차례로 jOOλ 연장 모두 튜플 (jOOλ부터) 기반 것을 순차 스트림 확장을 제공하며, 머리와 꼬리 추출 도메인 개체 (HeadAndTail) 솔루션.

    SequenceM.of(1,2,3,4)
             .splitAtHead(); //Tuple[1,SequenceM[2,3,4]
    
    SequenceM.of(1,2,3,4)
             .headAndTail();
    

    Tagir의 요청에 따라 업데이트 -> 스칼라의 자바 버전은 SequenceM를 사용하여 체

    public void sieveTest(){
        sieve(SequenceM.range(2, 1_000)).forEach(System.out::println);
    }
    
    SequenceM<Integer> sieve(SequenceM<Integer> s){
    
        return s.headAndTailOptional().map(ht ->SequenceM.of(ht.head())
                                .appendStream(sieve(ht.tail().filter(n -> n % ht.head() != 0))))
                        .orElse(SequenceM.of());
    }
    

    그리고 Streamable를 통해 다른 버전

    public void sieveTest2(){
        sieve(Streamable.range(2, 1_000)).forEach(System.out::println);
    }
    
    Streamable<Integer> sieve(Streamable<Integer> s){
    
        return s.size()==0? Streamable.of() : Streamable.of(s.head())
                                                        .appendStreamable(sieve(s.tail()
                                                                        .filter(n -> n % s.head() != 0)));
    }
    

    주 - 따라서 Streamable의의 크기 확인 및 headAndTailOptional의 사용 - SequenceM의 Streamable의도는 빈 구현이있다.

    마지막 버전은 일반 java.util.stream.Stream를 사용하여

    import static com.aol.cyclops.streams.StreamUtils.headAndTailOptional;
    
    public void sieveTest(){
        sieve(IntStream.range(2, 1_000).boxed()).forEach(System.out::println);
    }
    
    Stream<Integer> sieve(Stream<Integer> s){
    
        return headAndTailOptional(s).map(ht ->Stream.concat(Stream.of(ht.head())
                                ,sieve(ht.tail().filter(n -> n % ht.head() != 0))))
                        .orElse(Stream.of());
    }
    

    또 다른 업데이트 - 프리미티브가 아닌 객체를 사용하여 홀거 버전 @ 기반으로 게으른 반복적 인 (주 원시적 버전도 가능)

      final Mutable<Predicate<Integer>> predicate = Mutable.of(x->true);
      SequenceM.iterate(2, n->n+1)
               .filter(i->predicate.get().test(i))
               .peek(i->predicate.mutate(p-> p.and(v -> v%i!=0)))
               .limit(100000)
               .forEach(System.out::println);
    
  6. ==============================

    6.머리와 꼬리를 얻으려면 당신은 게으른 스트림 구현이 필요합니다. 자바 8 스트림 또는 RxJava는 적합하지 않습니다.

    머리와 꼬리를 얻으려면 당신은 게으른 스트림 구현이 필요합니다. 자바 8 스트림 또는 RxJava는 적합하지 않습니다.

    다음과 같이 예를 들어 LazySeq에 사용할 수 있습니다.

    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));
        }
    
    }
    
  7. ==============================

    7.여기 홀거에 의해 제안 된 방법을 사용하여 다른 조리법이다. 그냥 테이크 (int) 메소드 및 많은 다른 사람을 사용할 수있는 가능성을 추가 RxJava를 사용합니다.

    여기 홀거에 의해 제안 된 방법을 사용하여 다른 조리법이다. 그냥 테이크 (int) 메소드 및 많은 다른 사람을 사용할 수있는 가능성을 추가 RxJava를 사용합니다.

    package com.company;
    
    import rx.Observable;
    
    import java.util.function.IntPredicate;
    import java.util.stream.IntStream;
    
    public class Main {
    
        public static void main(String[] args) {
    
            final IntPredicate[] p={(x)->true};
            IntStream primesStream=IntStream.iterate(2,n->n+1).filter(i -> p[0].test(i)).peek(i->p[0]=p[0].and(v->v%i!=0)   );
    
            Observable primes = Observable.from(()->primesStream.iterator());
    
            primes.take(10).forEach((x) -> System.out.println(x.toString()));
    
    
        }
    
    }
    
  8. ==============================

    8.이 여기에 제공된 많은 흥미로운 제안이 있지만, 누군가가 타사 라이브러리에 의존하지 않고 솔루션을 필요로하는 경우 나는이 함께했다 :

    이 여기에 제공된 많은 흥미로운 제안이 있지만, 누군가가 타사 라이브러리에 의존하지 않고 솔루션을 필요로하는 경우 나는이 함께했다 :

        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)
            );
        }
    
  9. ==============================

    9.당신은, 스트림의 머리를 얻고 싶다면 :

    당신은, 스트림의 머리를 얻고 싶다면 :

    IntStream.range(1, 5).first();
    

    당신이 스트림의 꼬리를 얻고 싶은 경우에, 다만 :

    IntStream.range(1, 5).skip(1);
    

    당신은, 스트림의 양쪽 머리와 꼬리를 얻고 싶다면 :

    IntStream s = IntStream.range(1, 5);
    int head = s.head();
    IntStream tail = s.tail();
    

    당신이 총리를 찾으려면, 단지 :

    LongStream.range(2, n)
       .filter(i -> LongStream.range(2, (long) Math.sqrt(i) + 1).noneMatch(j -> i % j == 0))
       .forEach(N::println);
    

    더 알고 싶은 경우에, AbacusUtil를 얻을로 이동

    선언 : 나는 AbacusUtil의 개발자입니다.

  10. from https://stackoverflow.com/questions/19803058/java-8-stream-getting-head-and-tail by cc-by-sa and MIT license