스트림에서 연속 쌍 수집


102

스트림을 감안할 때 등 { 0, 1, 2, 3, 4 },

어떻게하면 그것을 주어진 형태로 가장 우아하게 바꿀 수 있습니까?

{ new Pair(0, 1), new Pair(1, 2), new Pair(2, 3), new Pair(3, 4) }

(물론 내가 Pair 클래스를 정의했다고 가정)?

편집 : 이것은 int 또는 원시 스트림에 관한 것이 아닙니다. 모든 유형의 스트림에 대한 대답은 일반적이어야합니다.


2
FP의 용어는 "파티션"이지만 Java에서 원하는 의미를 가진 메소드를 찾지 못했습니다. 술어에 대한 분할이 있습니다.
Marko Topolnik

1
일반적으로 JDK 8의 분할기는 순회 및 분할 목적으로 생각됩니다. 나는 또한 예를 생각 해보려고 노력할 것이다.
Olimpiu POP

list.stream().map(i -> new Pair(i, i+1));
aepurniet

2
스트림이 아닌 해당 질문에 대해서는 stackoverflow.com/questions/17453022/…를
Raedwald

그건 그렇고, 일부 사람들 Map.Entry은 Pair 클래스로 구현을 사용합니다 . (부여, 일부는 해킹 있다고 생각하지만, 수 사용하여 내장 클래스가 편리합니다.)
바질 우르 큐

답변:


33

표준 스트림을 확장하는 My StreamEx 라이브러리 pairMap는 모든 스트림 유형에 대한 방법을 제공합니다 . 원시 스트림의 경우 스트림 유형을 변경하지 않지만 일부 계산에 사용할 수 있습니다. 가장 일반적인 사용법은 차이를 계산하는 것입니다.

int[] pairwiseDiffs = IntStreamEx.of(input).pairMap((a, b) -> (b-a)).toArray();

개체 스트림의 경우 다른 개체 유형을 만들 수 있습니다. 내 라이브러리는 Pair(라이브러리 개념의 일부) 와 같은 새로운 사용자가 볼 수있는 데이터 구조를 제공하지 않습니다 . 그러나 자신의 Pair클래스가 있고이를 사용하려는 경우 다음을 수행 할 수 있습니다.

Stream<Pair> pairs = IntStreamEx.of(input).boxed().pairMap(Pair::new);

또는 이미 가지고있는 경우 Stream:

Stream<Pair> pairs = StreamEx.of(stream).pairMap(Pair::new);

이 기능은 사용자 지정 분할자를 사용하여 구현됩니다 . 오버 헤드가 매우 적고 잘 병렬화 할 수 있습니다. 물론 다른 많은 솔루션과 마찬가지로 임의 액세스 목록 / 배열뿐만 아니라 모든 스트림 소스에서 작동합니다. 많은 테스트에서 정말 잘 수행됩니다. 다음은 다른 접근 방식을 사용하여 더 큰 값 앞에 오는 모든 입력 값을 찾는 JMH 벤치 마크입니다 ( 질문 참조 ).


감사합니다! 이 도서관을 더 많이 공부할수록 더 좋아합니다. 드디어 스트림 사용을 시작할 수 있습니다. ( StreamEx구현 Iterable! 만세!)
Aleksandr Dubinsky 2015 년

답변을 100 % 완전하게 만들기 위해 Streama를 StreamEx?
Aleksandr Dubinsky 2015 년

3
@AleksandrDubinsky : 그냥 StreamEx.of(stream). Collection, 배열 Reader등에서 스트림을 만드는 다른 편리한 정적 메서드가 있습니다 . 답변을 수정했습니다.
Tagir Valeev 2015 년

@TagirValeev는 pairMap순차적 스트림에서 주문됩니까? 사실 forPairsOrdered ()를 갖고 싶지만 그런 방법이 없기 때문에 어떻게 든 시뮬레이션 할 수 있습니까? stream.ordered().forPairs()또는 stream().pairMap().forEachOrdered()?
Askar Kalykov

1
@AskarKalykov, pairMap비 간섭 상태 비 저장 매퍼 함수를 ​​사용하는 중간 작업이며, 단순 map. 은 forPairs사양에 의해 정렬되지 않은,하지만 정렬되지 않은 작업이 순차적 스트림 주문할 사실상이다. 더 많은 컨텍스트를 제공하기 위해 원래 문제를 별도의 스택 오버플로 질문으로 공식화하면 좋을 것입니다.
Tagir Valeev

74

Java 8 스트림 라이브러리는 주로 병렬 처리를 위해 스트림을 더 작은 청크로 분할하는 데 맞춰져 있으므로 상태 저장 파이프 라인 단계는 매우 제한적이며 현재 스트림 요소의 인덱스를 가져오고 인접한 스트림 요소에 액세스하는 것과 같은 작업은 지원되지 않습니다.

물론 이러한 문제를 해결하는 일반적인 방법은 몇 가지 제한 사항이 있지만 인덱스로 스트림을 구동하고 요소를 검색 할 수있는 ArrayList와 같은 일부 임의 액세스 데이터 구조에서 처리되는 값에 의존하는 것입니다. 값이에 있으면 arrayList다음과 같이 요청한대로 쌍을 생성 할 수 있습니다.

    IntStream.range(1, arrayList.size())
             .mapToObj(i -> new Pair(arrayList.get(i-1), arrayList.get(i)))
             .forEach(System.out::println);

물론 제한은 입력이 무한 스트림이 될 수 없다는 것입니다. 하지만이 파이프 라인은 병렬로 실행할 수 있습니다.


5
"입력은 무한 스트림이 될 수 없습니다." 실제로 입력은 스트림이 될 수 없습니다. 입력 ( arrayList)은 사실 모음이므로 답으로 표시하지 않았습니다. (하지만 골드 배지
Aleksandr Dubinsky

16

이것은 우아하지 않고 hackish 솔루션이지만 무한 스트림에서 작동합니다.

Stream<Pair> pairStream = Stream.iterate(0, (i) -> i + 1).map( // natural numbers
    new Function<Integer, Pair>() {
        Integer previous;

        @Override
        public Pair apply(Integer integer) {
            Pair pair = null;
            if (previous != null) pair = new Pair(previous, integer);
            previous = integer;
            return pair;
        }
    }).skip(1); // drop first null

이제 스트림을 원하는 길이로 제한 할 수 있습니다.

pairStream.limit(1_000_000).forEach(i -> System.out.println(i));

추신 : 클로저와 같은 더 나은 해결책이 있기를 바랍니다.(partition 2 1 stream)


6
익명 클래스가 때때로 람다에 대한 유용한 대안임을 지적한 것에 대한 찬사입니다.
Aleksandr Dubinsky

2
@aepurniet 제대로 작동하지 않을 것이라고 생각합니다. parallelStream문서 에 따르면 "올바른 동작을 유지하려면 이러한 동작 매개 변수가 방해가되지 않아야하며 대부분의 경우 상태 비 저장이어야합니다."
mishadoff

14
이것은 익명 함수가 상태 비 저장 이 아니기 때문에 스트림 프레임 워크의 디자인과 완전히 반대되며 맵 API의 계약을 직접 위반합니다 . 병렬 스트림과 더 많은 데이터로 이것을 실행하여 스트림 프레임 워크가 더 많은 작업 스레드를 생성하면 결과를 볼 수 있습니다. 드물게 발생하는 임의의 "오류"는 거의 재현 할 수없고 데이터가 충분할 때까지 감지하기 어렵습니다 (프로덕션에서?). 이것은 재앙이 될 수 있습니다.
Mario Rossi

4
@AleksandrDubinsky 제한 / 건너 뛰기가 병렬화 가능하다는 점이 틀 렸습니다. JDK에서 제공하는 구현은 실제로 병렬로 작동합니다. 작업이 발생 순서와 연결되어 있기 때문에 병렬화는 항상 성능 이점을 제공하지는 않지만 높은 Q 상황에서는 가능합니다.
Brian Goetz

4
@AleksandrDubinsky 틀 렸습니다. 스트림이 순서지정되지 않은 경우 임의의 요소를 건너 뛸 수 있습니다 (정의 된 발생 순서가 없으므로 논리적으로 "첫 번째"또는 "n 번째"요소 없으며 요소 만 있습니다.) 그러나 스트림의 순서가 지정되었는지 여부에 관계없이 항상 건너 뛸 수있었습니다. 병렬로 작업합니다. 스트림이 정렬 된 경우 추출 할 병렬 처리가 적지 만 여전히 병렬입니다.
Brian Goetz

15

원래 분할기에서 모든 n요소 T를 가져와 다음을 생성 하는 분할기 래퍼를 구현했습니다 List<T>.

public class ConsecutiveSpliterator<T> implements Spliterator<List<T>> {

    private final Spliterator<T> wrappedSpliterator;

    private final int n;

    private final Deque<T> deque;

    private final Consumer<T> dequeConsumer;

    public ConsecutiveSpliterator(Spliterator<T> wrappedSpliterator, int n) {
        this.wrappedSpliterator = wrappedSpliterator;
        this.n = n;
        this.deque = new ArrayDeque<>();
        this.dequeConsumer = deque::addLast;
    }

    @Override
    public boolean tryAdvance(Consumer<? super List<T>> action) {
        deque.pollFirst();
        fillDeque();
        if (deque.size() == n) {
            List<T> list = new ArrayList<>(deque);
            action.accept(list);
            return true;
        } else {
            return false;
        }
    }

    private void fillDeque() {
        while (deque.size() < n && wrappedSpliterator.tryAdvance(dequeConsumer))
            ;
    }

    @Override
    public Spliterator<List<T>> trySplit() {
        return null;
    }

    @Override
    public long estimateSize() {
        return wrappedSpliterator.estimateSize();
    }

    @Override
    public int characteristics() {
        return wrappedSpliterator.characteristics();
    }
}

연속 스트림을 생성하려면 다음 방법을 사용할 수 있습니다.

public <E> Stream<List<E>> consecutiveStream(Stream<E> stream, int n) {
    Spliterator<E> spliterator = stream.spliterator();
    Spliterator<List<E>> wrapper = new ConsecutiveSpliterator<>(spliterator, n);
    return StreamSupport.stream(wrapper, false);
}

샘플 사용법 :

consecutiveStream(Stream.of(0, 1, 2, 3, 4, 5), 2)
    .map(list -> new Pair(list.get(0), list.get(1)))
    .forEach(System.out::println);

모든 요소를 ​​두 번 반복합니까?
Aleksandr Dubinsky

아니. List<E>요소를 포함하는 새 스트림을 만듭니다 . 각 목록에는 n원래 스트림의 연속 요소가 포함 됩니다. 직접 확인하십시오;)
Tomek Rękawek

모든 요소 (첫 번째와 마지막 제외)가 반복되도록 답변을 수정할 수 있습니까?
Aleksandr Dubinsky

4
+1 좋은 작업이라고 생각하며 파티션 크기 외에도 모든 단계 크기로 일반화되어야합니다. (partition size step)함수에 대한 많은 필요가 있으며 이것이이를 얻는 가장 좋은 방법입니다.
Marko Topolnik 2014

3
ArrayDeque성능 을 위해를 사용 하는 것이 LinkedList좋습니다.
Marko Topolnik 2014

14

Stream.reduce () 메서드를 사용하여이 작업을 수행 할 수 있습니다 (이 기술을 사용하는 다른 답변은 본 적이 없음).

public static <T> List<Pair<T, T>> consecutive(List<T> list) {
    List<Pair<T, T>> pairs = new LinkedList<>();
    list.stream().reduce((a, b) -> {
        pairs.add(new Pair<>(a, b));
        return b;
    });
    return pairs;
}

1
(1,2) (3,4) 대신 (1,2) (2,3)을 반환합니다. 또한 순서대로 적용 될지 확실하지 않습니다 (확실히 보장되지 않습니다).
Aleksandr Dubinsky

1
@Aleksandr Dubinsky
SamTebbs33 2017 년

4
아, 네, 죄송합니다. 그리고 생각하기 위해 그것을 썼습니다.
Aleksandr Dubinsky


6

슬라이딩 연산자를 사용하여 cyclops-react (내가이 라이브러리에 기여 함)에서이를 수행 할 수 있습니다 .

  LazyFutureStream.of( 0, 1, 2, 3, 4 )
                  .sliding(2)
                  .map(Pair::new);

또는

   ReactiveSeq.of( 0, 1, 2, 3, 4 )
                  .sliding(2)
                  .map(Pair::new);

Pair 생성자가 2 개의 요소가있는 컬렉션을 받아 들일 수 있다고 가정합니다.

4로 그룹화하고 2로 증가하려는 경우에도 지원됩니다.

     ReactiveSeq.rangeLong( 0L,Long.MAX_VALUE)
                .sliding(4,2)
                .forEach(System.out::println);

java.util.stream.Stream을 통해 슬라이딩 뷰를 작성하기위한 동등한 정적 메소드는 cyclops-streams StreamUtils 클래스 에서도 제공됩니다 .

       StreamUtils.sliding(Stream.of(1,2,3,4),2)
                  .map(Pair::new);

참고 :-단일 스레드 작업의 경우 ReactiveSeq가 더 적합합니다. LazyFutureStream은 ReactiveSeq를 확장하지만 주로 동시 / 병렬 사용에 맞춰져 있습니다 (미래의 스트림입니다).

LazyFutureStream은 멋진 jOOλ (java.util.stream.Stream 확장)에서 Seq를 확장하는 ReactiveSeq를 확장하므로 Lukas가 제공하는 솔루션은 Stream 유형에서도 작동합니다. 관심있는 사람을 위해 창 / 슬라이딩 연산자 간의 주요 차이점은 명백한 상대적 전력 / 복잡도 절충 및 무한 스트림 사용에 대한 적합성입니다.


이렇게하면 [(0,1) (2,3) ...]을 얻지 만 질문은 [(0,1) (1,2) ...]를 묻습니다. RxJava로 내 대답을 참조하십시오 ...
frhack

1
당신 말이 맞아요, 제 잘못입니다. 질문을 잘못 읽었습니다. 여기서 슬라이딩 연산자가 올바른 것입니다. 내 답변을 업데이트하겠습니다-감사합니다!
존 소매점을

4

양성자 팩 라이브러리는 윈도우 된 functionnality을 제공합니다. Pair 클래스와 Stream이 주어지면 다음과 같이 할 수 있습니다.

Stream<Integer> st = Stream.iterate(0 , x -> x + 1);
Stream<Pair<Integer, Integer>> pairs = StreamUtils.windowed(st, 2, 1)
                                                  .map(l -> new Pair<>(l.get(0), l.get(1)))
                                                  .moreStreamOps(...);

이제 pairs스트림에는 다음이 포함됩니다.

(0, 1)
(1, 2)
(2, 3)
(3, 4)
(4, ...) and so on

그러나 st두 번 만들어야 할 것 같습니다 ! 이 라이브러리가 단일 스트림을 사용하여 문제를 해결할 수 있습니까?
Aleksandr Dubinsky 2015 년

@AleksandrDubinsky 나는 현재의 스플리터에서 사용할 수 있다고 생각하지 않습니다. 나는이 문제에 제출 github.com/poetix/protonpack/issues/9
알렉시스 C.

@AleksandrDubinsky windowed기능이 추가되었습니다! 편집을 참조하십시오.
Alexis C.

1
다른 사용자가 기록이 아닌 솔루션을 볼 수 있도록 이전 답변을 삭제하지 않겠습니까?
Aleksandr Dubinsky 2015 년

4

연속 쌍 찾기

타사 라이브러리를 사용하고 병렬 처리가 필요하지 않은 경우 jOOλ 는 다음 같은 SQL 스타일 창 함수를 제공합니다.

System.out.println(
Seq.of(0, 1, 2, 3, 4)
   .window()
   .filter(w -> w.lead().isPresent())
   .map(w -> tuple(w.value(), w.lead().get())) // alternatively, use your new Pair() class
   .toList()
);

굽힐 수 있는

[(0, 1), (1, 2), (2, 3), (3, 4)]

lead()함수는 창에서 순회 순서로 다음 값에 액세스합니다.

연속적인 트리플 / 쿼드 러플 / n- 튜플 찾기

댓글의 질문은 쌍이 아니라 n- 튜플 (또는 목록)을 수집해야하는보다 일반적인 솔루션을 요구하는 것입니다. 따라서 다음과 같은 대안이 있습니다.

int n = 3;

System.out.println(
Seq.of(0, 1, 2, 3, 4)
   .window(0, n - 1)
   .filter(w -> w.count() == n)
   .map(w -> w.window().toList())
   .toList()
);

목록 목록 생성

[[0, 1, 2], [1, 2, 3], [2, 3, 4]]

이 없으면 filter(w -> w.count() == n)결과는

[[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4], [4]]

면책 조항 : 나는 jOOλ 뒤에있는 회사에서 일합니다.


흥미 롭군. 3 개 이상의 요소를 그룹화해야하는 경우 어떻게합니까? 사용 w.lead().lead()?
Raul Santelices

1
@RaulSantelices : tuple(w.value(), w.lead(1), w.lead(2))옵션이 될 것입니다. 나는에 대한보다 일반적인 솔루션을 내 대답을 업데이 트했습니다length = n
루카스 에더

1
.window()전체 입력 스트림을 일부 중간 컬렉션으로 수집 한 다음 새 스트림을 만드는 게으른 작업이 아니라는 것이 올바르게 이해 됩니까?
Tagir Valeev

@TagirValeev : 예, 이것이 현재 구현입니다. 위의 경우 (더 Comparator다음과 같은 최적화, 재주문 창에 사용되지 않는다) 가능한 것, 그리고 가능성이 미래에 구현 될 것입니다.
Lukas Eder


2

RxJava (매우 강력한 리 액티브 확장 라이브러리)를 사용할 수 있습니다.

IntStream intStream  = IntStream.iterate(1, n -> n + 1);

Observable<List<Integer>> pairObservable = Observable.from(intStream::iterator).buffer(2,1);

pairObservable.take(10).forEach(b -> {
            b.forEach(n -> System.out.println(n));
            System.out.println();
        });

버퍼 연산자 피 감시로 방출한다 항목 방사하는 이러한 항목들의 집합을 버퍼링한다는 것을 피 감시 변환 ..


1
Observable.zip(obs, obs.skip(1), pair->{...})지금까지 사용 했습니다! Observable.buffer단계가있는 버전이 있는지 몰랐 습니다 (그리고 zip파이썬 의 트릭에 익숙합니다 ). +1
Reut Sharabani

1

작업은 본질적으로 상태 저장이므로 실제로 해결하려는 스트림이 아닙니다. javadoc 의 "Stateless Behaviors"섹션을 참조하십시오 .

가장 좋은 방법은 상태 저장 동작 매개 변수를 피하여 작업을 완전히 스트리밍하는 것입니다.

여기에서 한 가지 해결책은 외부 카운터를 통해 스트림에 상태를 도입하는 것입니다.하지만 순차 스트림에서만 작동합니다.

public static void main(String[] args) {
    Stream<String> strings = Stream.of("a", "b", "c", "c");
    AtomicReference<String> previous = new AtomicReference<>();
    List<Pair> collect = strings.map(n -> {
                            String p = previous.getAndSet(n);
                            return p == null ? null : new Pair(p, n);
                        })
                        .filter(p -> p != null)
                        .collect(toList());
    System.out.println(collect);
}


static class Pair<T> {
    private T left, right;
    Pair(T left, T right) { this.left = left; this.right = right; }
    @Override public String toString() { return "{" + left + "," + right + '}'; }
}

질문은 단순히 연속적인 정수를 수집하는 것이 아니라 입력 스트림의 연속적인 요소를 수집하도록 요청합니다. 용어에 대한 중요한 설명 : Stream! = "lambdas".
Aleksandr Dubinsky

AtomicInteger를 AtomicReference로 바꿀 수 있습니다. 대안은 자체 수집기를 롤링하거나 다음 예와 같은 외부 라이브러리를 사용하는 것입니다. stackoverflow.com/a/30090528/829571
assylias

내 편집을 참조하십시오. 또한 람다! = 스트림에 대한 귀하의 의견을 이해하지 못했습니다. 익명 클래스를 사용하는 다른 답변은 상태가 외부가 아닌 익명 클래스에 의해 유지된다는 점을 제외하고는 본질적으로 동일한 작업을 수행합니다.
assylias

1
작동합니다. StreamEx라이브러리는 좋은 발견하고 그 자체가 해답이 될 수 있습니다. "streams! = lambdas"에 대한 내 의견은 "이 작업은 본질적으로 상태 저장이므로 실제로 람다가 해결하려는 것이 아닙니다."라고 말합니다. "스트림"이라는 단어를 사용하려고하신 것 같습니다.
Aleksandr Dubinsky 2015 년

아, 알겠습니다. 분명히했습니다.
assylias

0

귀하의 경우에는 전달 된 마지막 int를 추적하는 사용자 지정 IntFunction을 작성하고이를 사용하여 원래 IntStream을 매핑합니다.

import java.util.function.IntFunction;
import java.util.stream.IntStream;

public class PairFunction implements IntFunction<PairFunction.Pair> {

  public static class Pair {

    private final int first;
    private final int second;

    public Pair(int first, int second) {
      this.first = first;
      this.second = second;
    }

    @Override
    public String toString() {
      return "[" + first + "|" + second + "]";
    }
  }

  private int last;
  private boolean first = true;

  @Override
  public Pair apply(int value) {
    Pair pair = !first ? new Pair(last, value) : null;
    last = value;
    first = false;
    return pair;
  }

  public static void main(String[] args) {

    IntStream intStream = IntStream.of(0, 1, 2, 3, 4);
    final PairFunction pairFunction = new PairFunction();
    intStream.mapToObj(pairFunction)
        .filter(p -> p != null) // filter out the null
        .forEach(System.out::println); // display each Pair

  }

}

이것의 문제는 무국적 상태가 창 밖으로 던져진다는 것입니다.
Rob

@Rob과 그 문제는 무엇입니까?
Aleksandr Dubinsky

람다의 주요 포인트 중 하나는 내부 적분기가 작업을 병렬 처리 할 수 ​​있도록 변경 가능한 상태가 없다는 것입니다.
Rob

@Rob : 예, 맞습니다.하지만 주어진 예제 스트림은 어쨌든 각 항목 (첫 번째 항목과 마지막 항목 제외)이 일부 쌍의 첫 번째 항목과 두 번째 항목으로 사용되므로 병렬 처리를 무시합니다.
jpvee 2013

@jpvee 예, 그게 당신이 생각하는 것이라고 생각했습니다. 다른 매퍼로 이것을 할 방법이 없는지 궁금합니다. 본질적으로 필요한 것은 루프 증분기를 2로 만든 다음 펑터가 2 개의 인수를 받도록하는 것과 같습니다. 가능해야합니다.
Rob

0

시계열의 시간 (x 값)의 연속적인 차이를 계산하기 위해 stream's collect(...)방법을 사용합니다 .

final List< Long > intervals = timeSeries.data().stream()
                    .map( TimeSeries.Datum::x )
                    .collect( DifferenceCollector::new, DifferenceCollector::accept, DifferenceCollector::combine )
                    .intervals();

DifferenceCollector는 다음과 같습니다.

public class DifferenceCollector implements LongConsumer
{
    private final List< Long > intervals = new ArrayList<>();
    private Long lastTime;

    @Override
    public void accept( final long time )
    {
        if( Objects.isNull( lastTime ) )
        {
            lastTime = time;
        }
        else
        {
            intervals.add( time - lastTime );
            lastTime = time;
        }
    }

    public void combine( final DifferenceCollector other )
    {
        intervals.addAll( other.intervals );
        lastTime = other.lastTime;
    }

    public List< Long > intervals()
    {
        return intervals;
    }
}

필요에 맞게 수정할 수 있습니다.


0

나는 마침내 Stream.reduce를 속여 값 쌍을 깔끔하게 처리 할 수있는 방법을 알아 냈습니다. JDK 8에서는 자연스럽게 나타나지 않는이 기능이 필요한 사용 사례가 많이 있습니다 .

public static int ArithGeo(int[] arr) {
    //Geometric
    List<Integer> diffList = new ArrayList<>();
    List<Integer> divList = new ArrayList<>();
    Arrays.stream(arr).reduce((left, right) -> {
        diffList.add(right-left);
        divList.add(right/left);
        return right;
    });
    //Arithmetic
    if(diffList.stream().distinct().count() == 1) {
        return 1;
    }
    //Geometric
    if(divList.stream().distinct().count() == 1) {
        return 2;
    }
    return -1;
}

내가 사용하는 트릭은 바로 반환입니다. 성명서.


1
나는 reduce이것이 작동하기 위해 충분한 보장을 한다고 생각하지 않는다 .
Aleksandr Dubinsky

충분한 보증 에 대해 더 알고 싶을 것 입니다. 자세히 설명해 주시겠습니까? 구아바에 대안이 있을지도 모르지만 ... 제약이있어서 사용할 수 없습니다.
Beezer 19.04.05

-1

우아한 해결책은 zip 을 사용하는 것 입니다. 다음과 같은 것 :

List<Integer> input = Arrays.asList(0, 1, 2, 3, 4);
Stream<Pair> pairStream = Streams.zip(input.stream(),
                                      input.stream().substream(1),
                                      (a, b) -> new Pair(a, b)
);

이것은 매우 간결하고 우아하지만 목록을 입력으로 사용합니다. 무한 스트림 소스는이 방법으로 처리 할 수 ​​없습니다.

또 다른 (훨씬 더 문제가되는) 문제는 전체 Streams 클래스와 함께 zip이 최근 API에서 제거되었다는 것입니다. 위의 코드는 b95 또는 이전 릴리스 에서만 작동 합니다. 따라서 최신 JDK에서는 우아한 FP 스타일 솔루션이 없다고 말하고 지금 당장은 zip이 API에 다시 도입되기를 바랄 수 있습니다.


실제로 zip제거되었습니다. Streams클래스 에 있던 모든 내용을 기억하지 못하지만 일부 항목이 Stream인터페이스의 정적 메서드로 마이그레이션되었으며 StreamSupportStream.Builder클래스 도 있습니다.
Stuart Marks

맞습니다. concat 또는 iterate와 같은 다른 메서드가 이동되어 Stream의 기본 메서드가되었습니다. 슬프게도 zip은 API에서 방금 제거되었습니다. 나는이 선택의 이유 (예 : Tuples 부족)를 이해하지만 여전히 좋은 기능이었습니다.
가제트

2
@gadget 튜플은 무엇과 관련이 zip있습니까? 어떤 현학적 인 이유가 만들어 졌다고해서 살인이 정당화되지는 않습니다 zip.
Aleksandr Dubinsky

@AleksandrDubinsky 대부분의 경우 zip은 Pairs / Tuples 모음을 출력으로 생성하는 데 사용됩니다. 그들은 zip을 유지하면 사람들이 JDK의 일부로 Tuples를 요청할 것이라고 주장 했습니다. 그래도 기존 기능을 제거하지 않았을 것입니다.
가제트

-1

이것은 흥미로운 문제입니다. 내 하이브리드 시도가 좋은가요?

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(1, 2, 3);
    Iterator<Integer> first = list.iterator();
    first.next();
    if (first.hasNext())
        list.stream()
        .skip(1)
        .map(v -> new Pair(first.next(), v))
        .forEach(System.out::println);
}

나는 그것이 병렬 처리에 적합하지 않기 때문에 실격 될 수 있다고 생각합니다.


문제는 병렬 처리를 요청하지 못했지만, 우리가 단지가 있다고 가정 않은 Stream하는하지 List. 물론 우리는 Stream에서도 반복자를 검색 할 수 있으므로 유효한 해결책이 될 수 있습니다. 그럼에도 불구하고 이것은 독창적 인 접근 방식입니다.
Aleksandr Dubinsky

-1

다른 사람들이 관찰 한 것처럼 문제의 특성으로 인해 일부 상태 저장이 필요합니다.

필자는 본질적으로 Oracle SQL 함수 LEAD를 원했던 비슷한 문제에 직면했습니다. 그것을 구현하려는 나의 시도는 다음과 같습니다.

/**
 * Stream that pairs each element in the stream with the next subsequent element.
 * The final pair will have only the first item, the second will be null.
 */
<T> Spliterator<Pair<T>> lead(final Stream<T> stream)
{
    final Iterator<T> input = stream.sequential().iterator();

    final Iterable<Pair<T>> iterable = () ->
    {
        return new Iterator<Pair<T>>()
        {
            Optional<T> current = getOptionalNext(input);

            @Override
            public boolean hasNext()
            {
                return current.isPresent();
            }

            @Override
            public Pair<T> next()
            {
                Optional<T> next = getOptionalNext(input);
                final Pair<T> pair = next.isPresent()
                    ? new Pair(current.get(), next.get())
                    : new Pair(current.get(), null);
                current = next;

                return pair;
            }
        };
    };

    return iterable.spliterator();
}

private <T> Optional<T> getOptionalNext(final Iterator<T> iterator)
{
    return iterator.hasNext()
        ? Optional.of(iterator.next())
        : Optional.empty();
}

-1

제한된 큐를 사용하여 스트림을 통해 흐르는 요소를 저장함으로써이를 달성 할 수 있습니다 (여기에서 자세히 설명한 아이디어를 기반으로합니다 . 스트림에서 다음 요소를 가져올 수 있습니까?). ).

아래 예제는 먼저 스트림을 통과하는 요소를 저장할 BoundedQueue 클래스의 인스턴스를 정의합니다 (LinkedList 확장에 대한 아이디어가 마음에 들지 않으면 위에서 언급 한 대체 및보다 일반적인 접근 방식을 참조하십시오). 나중에 두 개의 후속 요소를 Pair의 인스턴스로 결합합니다.

public class TwoSubsequentElems {
  public static void main(String[] args) {
    List<Integer> input = new ArrayList<Integer>(asList(0, 1, 2, 3, 4));

    class BoundedQueue<T> extends LinkedList<T> {
      public BoundedQueue<T> save(T curElem) {
        if (size() == 2) { // we need to know only two subsequent elements
          pollLast(); // remove last to keep only requested number of elements
        }

        offerFirst(curElem);

        return this;
      }

      public T getPrevious() {
        return (size() < 2) ? null : getLast();
      }

      public T getCurrent() {
        return (size() == 0) ? null : getFirst();
      }
    }

    BoundedQueue<Integer> streamHistory = new BoundedQueue<Integer>();

    final List<Pair<Integer>> answer = input.stream()
      .map(i -> streamHistory.save(i))
      .filter(e -> e.getPrevious() != null)
      .map(e -> new Pair<Integer>(e.getPrevious(), e.getCurrent()))
      .collect(Collectors.toList());

    answer.forEach(System.out::println);
  }
}

-3

@aepurniet에 동의하지만 대신 mapToObj를 사용해야합니다.

range(0, 100).mapToObj((i) -> new Pair(i, i+1)).forEach(System.out::println);

1
권리. 그러나 이것은 (모든 유형의) 스트림 요소 쌍이 아닌 정수 쌍을 수집합니다.
Aleksandr Dubinsky

-5

for0에서 length-1스트림 까지 실행되는 루프 실행

for(int i = 0 ; i < stream.length-1 ; i++)
{
    Pair pair = new Pair(stream[i], stream[i+1]);
    // then add your pair to an array
}

3
그리고 솔루션의 람다 부분은 어디에 있습니까?
Olimpiu POP

스트림이 무한한 경우에는 해당되지 않습니다
mishadoff

@Olimpiu-람다를 어디서 얻었습니까? 나는 내가 그것을 놓치고 있지 않은지 확인하기 위해 질문을 두 번 읽었다. 편집 내역도 확인했습니다. 그리고 질문에는 태그가 없습니다.
jww
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.