[java] 자바 8 스트림 역순

일반적인 질문 : 스트림을 되 돌리는 올바른 방법은 무엇입니까? 스트림이 어떤 유형의 요소로 구성되어 있는지 알지 못한다면 어떤 스트림을 역전시키는 일반적인 방법은 무엇입니까?

구체적인 질문 :

IntStream특정 범위에서 정수를 생성하는 범위 방법을 제공합니다 IntStream.range(-range, 0). 이제 0에서 음수로 전환 범위를 반대로 바꾸면 작동하지 않습니다.Integer::compare

List<Integer> list = Arrays.asList(1,2,3,4);
list.stream().sorted(Integer::compare).forEach(System.out::println);

IntStream나는이 컴파일러 오류가 발생합니다

오류 : (191, 0) ajc : sorted()형식 의 메서드 IntStream는 인수 ( Integer::compare)에 적용 할 수 없습니다.

내가 여기서 무엇을 놓치고 있습니까?



답변

reverse 생성에 대한 특정 질문에 대해서는 IntStream다음과 같이 시도하십시오.

static IntStream revRange(int from, int to) {
    return IntStream.range(from, to)
                    .map(i -> to - i + from - 1);
}

이것은 권투와 정렬을 피합니다.

모든 유형의 스트림을 되 돌리는 방법에 대한 일반적인 질문에 대해서는 “적절한”방법이 있는지 모르겠습니다. 내가 생각할 수있는 몇 가지 방법이 있습니다. 둘 다 스트림 요소를 저장합니다. 요소를 저장하지 않고 스트림을 되돌릴 수있는 방법을 모르겠습니다.

이 첫 번째 방법은 요소를 배열에 저장하고 역순으로 스트림으로 읽습니다. 스트림 요소의 런타임 유형을 모르기 때문에 배열을 올바르게 입력 할 수 없으므로 확인되지 않은 캐스트가 필요합니다.

@SuppressWarnings("unchecked")
static <T> Stream<T> reverse(Stream<T> input) {
    Object[] temp = input.toArray();
    return (Stream<T>) IntStream.range(0, temp.length)
                                .mapToObj(i -> temp[temp.length - i - 1]);
}

다른 기술은 콜렉터를 사용하여 항목을 역순으로 누적합니다. 이것은 ArrayList객체 앞에 많은 삽입을 수행 하므로 많은 복사가 진행됩니다.

Stream<T> input = ... ;
List<T> output =
    input.collect(ArrayList::new,
                  (list, e) -> list.add(0, e),
                  (list1, list2) -> list1.addAll(0, list2));

일종의 맞춤형 데이터 구조를 사용하여 훨씬 더 효율적인 리버 싱 콜렉터를 작성할 수 있습니다.

업데이트 2016-01-29

이 질문은 최근에 약간의 주목을 받았으므로, 나는 앞에 삽입하는 문제를 해결하기 위해 대답을 업데이트해야한다고 생각합니다. ArrayList . 이것은 많은 요소로 인해 비효율적이며 O (N ^ 2) 복사가 필요합니다.

ArrayDeque전면의 삽입을 효율적으로 지원 하는 대신 대신 사용하는 것이 좋습니다. 작은 주름은 우리가 3 중 형태를 사용할 수 없다는 것입니다 Stream.collect(). 두 번째 arg의 내용을 첫 번째 arg로 병합해야하며에 “앞에 추가”대량 작업이 없습니다 Deque. 대신 addAll()첫 번째 인수의 내용을 두 번째 끝에 추가 한 다음 두 번째를 반환합니다. 이를 사용하려면Collector.of()공장 방법을 .

완전한 코드는 다음과 같습니다.

Deque<String> output =
    input.collect(Collector.of(
        ArrayDeque::new,
        (deq, t) -> deq.addFirst(t),
        (d1, d2) -> { d2.addAll(d1); return d2; }));

결과는의 Deque대신 List이지만 지금 반전 된 순서로 쉽게 반복하거나 스트리밍 할 수 있기 때문에 큰 문제는 아닙니다.


답변

우아한 솔루션

List<Integer> list = Arrays.asList(1,2,3,4);
list.stream()
    .boxed() // Converts Intstream to Stream<Integer>
    .sorted(Collections.reverseOrder()) // Method on Stream<Integer>
    .forEach(System.out::println);


답변

여기에있는 많은 솔루션이을 정렬하거나 반대로 IntStream했지만 불필요하게 중간 저장소가 필요합니다. 스튜어트 마크의 솔루션 은 다음과 같습니다.

static IntStream revRange(int from, int to) {
    return IntStream.range(from, to).map(i -> to - i + from - 1);
}

오버플로도 올바르게 처리하여 다음 테스트를 통과합니다.

@Test
public void testRevRange() {
    assertArrayEquals(revRange(0, 5).toArray(), new int[]{4, 3, 2, 1, 0});
    assertArrayEquals(revRange(-5, 0).toArray(), new int[]{-1, -2, -3, -4, -5});
    assertArrayEquals(revRange(1, 4).toArray(), new int[]{3, 2, 1});
    assertArrayEquals(revRange(0, 0).toArray(), new int[0]);
    assertArrayEquals(revRange(0, -1).toArray(), new int[0]);
    assertArrayEquals(revRange(MIN_VALUE, MIN_VALUE).toArray(), new int[0]);
    assertArrayEquals(revRange(MAX_VALUE, MAX_VALUE).toArray(), new int[0]);
    assertArrayEquals(revRange(MIN_VALUE, MIN_VALUE + 1).toArray(), new int[]{MIN_VALUE});
    assertArrayEquals(revRange(MAX_VALUE - 1, MAX_VALUE).toArray(), new int[]{MAX_VALUE - 1});
}


답변

일반적인 질문 :

스트림은 요소를 저장하지 않습니다.

따라서 일부 중간 컬렉션에 요소를 저장하지 않으면 요소를 역순으로 반복 할 수 없습니다.

Stream.of("1", "2", "20", "3")
      .collect(Collectors.toCollection(ArrayDeque::new)) // or LinkedList
      .descendingIterator()
      .forEachRemaining(System.out::println);

업데이트 : LinkedList를 ArrayDeque로 변경했습니다 (더 나은) 자세한 내용은 여기를 참조하십시오

인쇄물:

3

20

2

1

그건 그렇고, 방법을 사용 sort하면 정렬 할 때 정확하지 않으며 반대는 아닙니다 (스트림에 정렬되지 않은 요소가 있다고 가정)

구체적인 질문 :

이 간단하고 쉽고 직관적 인 것을 발견했습니다 (@Holger 의견 복사 )

IntStream.iterate(to - 1, i -> i - 1).limit(to - from)


답변

외부 라이브러리없이 …

import java.util.List;
import java.util.Collections;
import java.util.stream.Collector;

public class MyCollectors {

    public static <T> Collector<T, ?, List<T>> toListReversed() {
        return Collectors.collectingAndThen(Collectors.toList(), l -> {
            Collections.reverse(l);
            return l;
        });
    }

}


답변

구현 경우 Comparable<T>(예. Integer, String, Date), 당신은 사용하여 작업을 수행 할 수 있습니다 Comparator.reverseOrder().

List<Integer> list = Arrays.asList(1, 2, 3, 4);
list.stream()
     .sorted(Comparator.reverseOrder())
     .forEach(System.out::println);


답변

요소를 역순으로 수집하는 자체 수집기를 정의 할 수 있습니다.

public static <T> Collector<T, List<T>, List<T>> inReverse() {
    return Collector.of(
        ArrayList::new,
        (l, t) -> l.add(t),
        (l, r) -> {l.addAll(r); return l;},
        Lists::<T>reverse);
}

그리고 그것을 다음과 같이 사용하십시오 :

stream.collect(inReverse()).forEach(t -> ...)

목록의 끝에 수집 항목을 효율적으로 삽입하기 위해 ArrayList를 앞으로 사용하고 Guava Lists.reverse를 사용하여 목록의 다른 사본을 만들지 않고 목록의 역전을 효율적으로 제공합니다.

다음은 사용자 지정 수집기의 테스트 사례입니다.

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;

import org.hamcrest.Matchers;
import org.junit.Test;

import com.google.common.collect.Lists;

public class TestReverseCollector {
    private final Object t1 = new Object();
    private final Object t2 = new Object();
    private final Object t3 = new Object();
    private final Object t4 = new Object();

    private final Collector<Object, List<Object>, List<Object>> inReverse = inReverse();
    private final Supplier<List<Object>> supplier = inReverse.supplier();
    private final BiConsumer<List<Object>, Object> accumulator = inReverse.accumulator();
    private final Function<List<Object>, List<Object>> finisher = inReverse.finisher();
    private final BinaryOperator<List<Object>> combiner = inReverse.combiner();

    @Test public void associative() {
        final List<Object> a1 = supplier.get();
        accumulator.accept(a1, t1);
        accumulator.accept(a1, t2);
        final List<Object> r1 = finisher.apply(a1);

        final List<Object> a2 = supplier.get();
        accumulator.accept(a2, t1);
        final List<Object> a3 = supplier.get();
        accumulator.accept(a3, t2);
        final List<Object> r2 = finisher.apply(combiner.apply(a2, a3));

        assertThat(r1, Matchers.equalTo(r2));
    }

    @Test public void identity() {
        final List<Object> a1 = supplier.get();
        accumulator.accept(a1, t1);
        accumulator.accept(a1, t2);
        final List<Object> r1 = finisher.apply(a1);

        final List<Object> a2 = supplier.get();
        accumulator.accept(a2, t1);
        accumulator.accept(a2, t2);
        final List<Object> r2 = finisher.apply(combiner.apply(a2, supplier.get()));

        assertThat(r1, equalTo(r2));
    }

    @Test public void reversing() throws Exception {
        final List<Object> a2 = supplier.get();
        accumulator.accept(a2, t1);
        accumulator.accept(a2, t2);

        final List<Object> a3 = supplier.get();
        accumulator.accept(a3, t3);
        accumulator.accept(a3, t4);

        final List<Object> r2 = finisher.apply(combiner.apply(a2, a3));

        assertThat(r2, contains(t4, t3, t2, t1));
    }

    public static <T> Collector<T, List<T>, List<T>> inReverse() {
        return Collector.of(
            ArrayList::new,
            (l, t) -> l.add(t),
            (l, r) -> {l.addAll(r); return l;},
            Lists::<T>reverse);
    }
}