programing

술어로 스트림 제한

prostudy 2022. 5. 13. 23:49
반응형

술어로 스트림 제한

a(잠재적으로 무한 확장)를 제한하는 Java 8 스트림 작업이 있는가?Stream첫 번째 요소가 술어와 일치하지 않을 때까지?

Java 9에서 우리는 사용할 수 있다.takeWhile아래 예시와 같이 10보다 작은 모든 숫자를 인쇄한다.

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

자바 8에는 그런 운영이 없기 때문에 일반적인 방법으로 실행하는 것이 가장 좋은 방법은 무엇일까?

takeWhile그리고dropWhileJDK 9에 추가됨. 예시 코드

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

JDK 9에 따라 컴파일되고 실행될 때 당신이 예상하는 대로 정확하게 행동할 것이다.

JDK 9가 나왔다.여기에서 JDK 9 릴리즈를 다운로드할 수 있다.

그러한 작전은 자바 8로 가능해야 한다.Stream, 그러나 그것이 반드시 효율적으로 수행될 수는 없다. 예를 들어, 요소들을 순서대로 살펴야 하는 것처럼, 그러한 작업을 반드시 병행할 수는 없다.

API가 쉬운 방법은 아니지만, 가장 간단한 방법은Stream.iterator(), "Take-the-time" 구현을 위해 포장한 다음 다시 로 돌아가십시오.Spliterator그리고 나서 aStream. 또는 -- 아마도 -- 이 구현에서는 더 이상 분할할 수 없지만 포장하십시오.

되지 않은 스스지지은의 takeWhile에서Spliterator:

static <T> Spliterator<T> takeWhile(
    Spliterator<T> splitr, Predicate<? super T> predicate) {
  return new Spliterators.AbstractSpliterator<T>(splitr.estimateSize(), 0) {
    boolean stillGoing = true;
    @Override public boolean tryAdvance(Consumer<? super T> consumer) {
      if (stillGoing) {
        boolean hadNext = splitr.tryAdvance(elem -> {
          if (predicate.test(elem)) {
            consumer.accept(elem);
          } else {
            stillGoing = false;
          }
        });
        return hadNext && stillGoing;
      }
      return false;
    }
  };
}

static <T> Stream<T> takeWhile(Stream<T> stream, Predicate<? super T> predicate) {
   return StreamSupport.stream(takeWhile(stream.spliterator(), predicate), false);
}

allMatch()단락 기능이기 때문에, 당신은 그것을 처리를 멈추는데 사용할 수 있다.가장 큰 단점은 시험을 두 번 해야 한다는 것이다. 시험을 한 번 치러야 하는지, 그리고 계속 진행해야 하는지 다시 한 번.

IntStream
    .iterate(1, n -> n + 1)
    .peek(n->{if (n<10) System.out.println(n);})
    .allMatch(n->n < 10);

@StuartMarks의 후속 답변으로.StreamEx 라이브러리에는 현재 JDK-9 구현과 호환되는 작업이 있다.JDK-9에서 실행될 경우 JDK 구현에 위임만 할 뿐(이를 통해)MethodHandle.invokeExact그것은 정말 빠르다.JDK-8로 실행될 경우, "폴리필" 구현이 사용될 것이다.그래서 내 도서관을 이용하면 다음과 같이 문제를 해결할 수 있다.

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

takeWhile프로토라이브러리가 제공하는 기능 중 하나이다.

Stream<Integer> infiniteInts = Stream.iterate(0, i -> i + 1);
Stream<Integer> finiteInts = StreamUtils.takeWhile(infiniteInts, i -> i < 10);

assertThat(finiteInts.collect(Collectors.toList()),
           hasSize(10));

9 업이이: Java 9Stream이제 곧 기회가 온다.방법론에서.

해킹이나 다른 해결책이 필요하지 않다.그냥 그거 써!


나는 이것이 크게 개선될 수 있다고 확신한다: (누군가가 그것을 안전하게 만들 수 있을지도 모른다)

Stream<Integer> stream = Stream.iterate(0, n -> n + 1);

TakeWhile.stream(stream, n -> n < 10000)
         .forEach(n -> System.out.print((n == 0 ? "" + n : "," + n)));

해킹은 확실히...우아하지는 않지만 ~:D로 작동함

class TakeWhile<T> implements Iterator<T> {

    private final Iterator<T> iterator;
    private final Predicate<T> predicate;
    private volatile T next;
    private volatile boolean keepGoing = true;

    public TakeWhile(Stream<T> s, Predicate<T> p) {
        this.iterator = s.iterator();
        this.predicate = p;
    }

    @Override
    public boolean hasNext() {
        if (!keepGoing) {
            return false;
        }
        if (next != null) {
            return true;
        }
        if (iterator.hasNext()) {
            next = iterator.next();
            keepGoing = predicate.test(next);
            if (!keepGoing) {
                next = null;
            }
        }
        return next != null;
    }

    @Override
    public T next() {
        if (next == null) {
            if (!hasNext()) {
                throw new NoSuchElementException("Sorry. Nothing for you.");
            }
        }
        T temp = next;
        next = null;
        return temp;
    }

    public static <T> Stream<T> stream(Stream<T> s, Predicate<T> p) {
        TakeWhile tw = new TakeWhile(s, p);
        Spliterator split = Spliterators.spliterator(tw, Integer.MAX_VALUE, Spliterator.ORDERED);
        return StreamSupport.stream(split, false);
    }

}

자바8 + rxjava를 사용할 수 있다.

import java.util.stream.IntStream;
import rx.Observable;


// Example 1)
IntStream intStream  = IntStream.iterate(1, n -> n + 1);
Observable.from(() -> intStream.iterator())
    .takeWhile(n ->
          {
                System.out.println(n);
                return n < 10;
          }
    ).subscribe() ;


// Example 2
IntStream intStream  = IntStream.iterate(1, n -> n + 1);
Observable.from(() -> intStream.iterator())
    .takeWhile(n -> n < 10)
    .forEach( n -> System.out.println(n));

실제로 별도의 라이브러리 없이 자바 8에서 하는 방법과 자바 9를 사용하는 방법에는 두 가지가 있다.

콘솔에서 2에서 20까지의 숫자를 인쇄하려면 다음을 수행하십시오.

IntStream.iterate(2, (i) -> i + 2).peek(System.out::println).allMatch(i -> i < 20);

또는

IntStream.iterate(2, (i) -> i + 2).peek(System.out::println).anyMatch(i -> i >= 20);

출력은 두 가지 경우 모두 다음과 같다.

2
4
6
8
10
12
14
16
18
20

아직 아무도 Match에 대해 언급하지 않았다.이것이 이 게시물의 이유야.

이것은 JDK 9 java.util.stream에서 복사한 원본이다.스트림.테이크한편(Predicate).JDK 8과 함께 일하기 위해서는 약간의 차이가 있다.

static <T> Stream<T> takeWhile(Stream<T> stream, Predicate<? super T> p) {
    class Taking extends Spliterators.AbstractSpliterator<T> implements Consumer<T> {
        private static final int CANCEL_CHECK_COUNT = 63;
        private final Spliterator<T> s;
        private int count;
        private T t;
        private final AtomicBoolean cancel = new AtomicBoolean();
        private boolean takeOrDrop = true;

        Taking(Spliterator<T> s) {
            super(s.estimateSize(), s.characteristics() & ~(Spliterator.SIZED | Spliterator.SUBSIZED));
            this.s = s;
        }

        @Override
        public boolean tryAdvance(Consumer<? super T> action) {
            boolean test = true;
            if (takeOrDrop &&               // If can take
                    (count != 0 || !cancel.get()) && // and if not cancelled
                    s.tryAdvance(this) &&   // and if advanced one element
                    (test = p.test(t))) {   // and test on element passes
                action.accept(t);           // then accept element
                return true;
            } else {
                // Taking is finished
                takeOrDrop = false;
                // Cancel all further traversal and splitting operations
                // only if test of element failed (short-circuited)
                if (!test)
                    cancel.set(true);
                return false;
            }
        }

        @Override
        public Comparator<? super T> getComparator() {
            return s.getComparator();
        }

        @Override
        public void accept(T t) {
            count = (count + 1) & CANCEL_CHECK_COUNT;
            this.t = t;
        }

        @Override
        public Spliterator<T> trySplit() {
            return null;
        }
    }
    return StreamSupport.stream(new Taking(stream.spliterator()), stream.isParallel()).onClose(stream::close);
}

여기 ints에서 한 버전이 있다 - 질문에서 물어본 대로.

사용량:

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

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

라이브러리 AbacusUtil을 다운로드하십시오.원하는 API 및 기타 기능을 제공한다.

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

선언: 나는 아바쿠스유틸의 개발자다.

수행될 정확한 회수의 양을 알면 할 수 있다.

IntStream
          .iterate(1, n -> n + 1)
          .limit(10)
          .forEach(System.out::println);
    IntStream.iterate(1, n -> n + 1)
    .peek(System.out::println) //it will be executed 9 times
    .filter(n->n>=9)
    .findAny();

peak 대신 mapToObj를 사용하여 최종 객체 또는 메시지를 반환할 수 있음

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

일부 스트림 값은 값에 관계없이 처리되지 않는 단락 단자 작동 외에는 스트림을 중단할 수 없다.그러나 스트림에서 작업을 수행하지 않으려면 스트림에 변환 및 필터를 추가하십시오.

import java.util.Objects;

class ThingProcessor
{
    static Thing returnNullOnCondition(Thing thing)
    {    return( (*** is condition met ***)? null : thing);    }

    void processThings(Collection<Thing> thingsCollection)
    {
        thingsCollection.stream()
        *** regular stream processing ***
        .map(ThingProcessor::returnNullOnCondition)
        .filter(Objects::nonNull)
        *** continue stream processing ***
    }
} // class ThingProcessor

어떤 조건을 충족하면 사물의 흐름을 무효로 바꾼 다음, 무효를 걸러낸다.부작용에 빠져들 의향이 있다면 일단 어떤 것이 부딪히면 조건 값을 참으로 설정할 수 있기 때문에 이후의 모든 것들은 그 가치에 관계없이 걸러진다.그러나 그렇지 않더라도 처리하지 않으려는 스트림에서 값을 필터링하여 많은(전부는 아닐 경우) 처리를 저장할 수 있다.

비슷한 요구 사항이 있었는데도 웹 서비스를 호출하고, 실패할 경우 3번 다시 시도하십시오.이렇게 많은 시행 후에도 실패하면 이메일 알림을 보내십시오.구글을 많이 검색해보니anyMatch()구세주로서 왔다.나의 샘플 코드는 다음과 같다.다음 예에서 webServiceCall 방법이 첫 번째 반복 자체에서 true로 반환되는 경우 스트림은 우리가 호출한 대로 더 이상 반복되지 않는다.anyMatch()난 네가 찾고 있는 게 바로 이것이라고 믿어.

import java.util.stream.IntStream;

import io.netty.util.internal.ThreadLocalRandom;

class TrialStreamMatch {

public static void main(String[] args) {        
    if(!IntStream.range(1,3).anyMatch(integ -> webServiceCall(integ))){
         //Code for sending email notifications
    }
}

public static boolean webServiceCall(int i){
    //For time being, I have written a code for generating boolean randomly
    //This whole piece needs to be replaced by actual web-service client code
    boolean bool = ThreadLocalRandom.current().nextBoolean();
    System.out.println("Iteration index :: "+i+" bool :: "+bool);

    //Return success status -- true or false
    return bool;
}

만약 당신이 다른 문제를 가지고 있다면, 다른 해결책이 필요할지도 모르지만, 당신의 현재 문제에 대해서, 나는 간단히 다음과 같이 할 것이다.

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

주제에서 약간 벗어나겠지만, 이것이 우리가 원하는 것이다.List<T>Stream<T>.

먼저, 당신은 A를 가져야 한다.take활용 방법이 방법이 우선이다.n요소:

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

그것은 그냥 처럼 작동한다.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));

이제 a를 쓰는 것은 꽤 간단할 것이다.takeWhile에 기반을 둔 방법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
}

다음과 같이 작동한다.

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

이 구현은 목록을 부분적으로 몇 번 반복하지만 추가하지는 않는다.O(n^2) 바란다그게 받아들여지길 바래.

나는 이것을 실행함으로써 또 다른 빠른 해결책을 가지고 있다(사실 부정적이지만, 당신은 그 생각을 알 수 있다).

public static void main(String[] args) {
    System.out.println(StreamUtil.iterate(1, o -> o + 1).terminateOn(15)
            .map(o -> o.toString()).collect(Collectors.joining(", ")));
}

static interface TerminatedStream<T> {
    Stream<T> terminateOn(T e);
}

static class StreamUtil {
    static <T> TerminatedStream<T> iterate(T seed, UnaryOperator<T> op) {
        return new TerminatedStream<T>() {
            public Stream<T> terminateOn(T e) {
                Builder<T> builder = Stream.<T> builder().add(seed);
                T current = seed;
                while (!current.equals(e)) {
                    current = op.apply(current);
                    builder.add(current);
                }
                return builder.build();
            }
        };
    }
}

자바 스트림 라이브러리만 사용해 보십시오.

        IntStream.iterate(0, i -> i + 1)
        .filter(n -> {
                if (n < 10) {
                    System.out.println(n);
                    return false;
                } else {
                    return true;
                }
            })
        .findAny();

참조URL: https://stackoverflow.com/questions/20746429/limit-a-stream-by-a-predicate

반응형