[java] 두 개의 Java 8 스트림 또는 추가 요소를 스트림에 추가

다음과 같이 스트림이나 추가 요소를 추가 할 수 있습니다.

Stream stream = Stream.concat(stream1, Stream.concat(stream2, Stream.of(element));

다음과 같이 새로운 것을 추가 할 수 있습니다.

Stream stream = Stream.concat(
                       Stream.concat(
                              stream1.filter(x -> x!=0), stream2)
                              .filter(x -> x!=1),
                                  Stream.of(element))
                                  .filter(x -> x!=2);

그러나 concat정적 이기 때문에 이것은 추악 합니다. concat인스턴스 방법 이라면 위의 예제를 훨씬 쉽게 읽을 수 있습니다.

 Stream stream = stream1.concat(stream2).concat(element);

 Stream stream = stream1
                 .filter(x -> x!=0)
                 .concat(stream2)
                 .filter(x -> x!=1)
                 .concat(element)
                 .filter(x -> x!=2);

내 질문은 :

1) 왜 concat정적 인 이유 가 있습니까? 아니면 내가 누락 된 동등한 인스턴스 메소드가 있습니까?

2) 어쨌든 더 좋은 방법이 있습니까?



답변

Stream.concatStream.of에 대한 정적 가져 오기 를 추가 하면 첫 번째 예제를 다음과 같이 작성할 수 있습니다.

Stream<Foo> stream = concat(stream1, concat(stream2, of(element)));

일반 이름으로 정적 메소드 를 가져 오면 코드를 읽고 유지하기가 어려워 질 수 있습니다 ( 네임 스페이스 오염 ). 따라서 보다 의미있는 이름으로 고유 한 정적 메서드 를 만드는 것이 좋습니다 . 그러나 데모를 위해이 이름을 사용합니다.

public static <T> Stream<T> concat(Stream<? extends T> lhs, Stream<? extends T> rhs) {
    return Stream.concat(lhs, rhs);
}
public static <T> Stream<T> concat(Stream<? extends T> lhs, T rhs) {
    return Stream.concat(lhs, Stream.of(rhs));
}

이 두 가지 정적 메소드 (선택적으로 정적 가져 오기와 함께 사용)를 사용하여 두 가지 예를 다음과 같이 작성할 수 있습니다.

Stream<Foo> stream = concat(stream1, concat(stream2, element));

Stream<Foo> stream = concat(
                         concat(stream1.filter(x -> x!=0), stream2).filter(x -> x!=1),
                         element)
                     .filter(x -> x!=2);

코드가 훨씬 짧아졌습니다. 그러나 가독성이 향상되지 않았다는 데 동의합니다. 그래서 다른 해결책이 있습니다.


많은 상황에서 수집기 는 스트림 기능 을 확장 하는 데 사용될 수 있습니다 . 두 수집기 가 맨 아래에 있으면 두 가지 예를 다음과 같이 작성할 수 있습니다.

Stream<Foo> stream = stream1.collect(concat(stream2)).collect(concat(element));

Stream<Foo> stream = stream1
                     .filter(x -> x!=0)
                     .collect(concat(stream2))
                     .filter(x -> x!=1)
                     .collect(concat(element))
                     .filter(x -> x!=2);

원하는 구문과 위 구문의 유일한 차이점은 concat (…)collect (concat (…)) 로 바꿔야한다는 것 입니다. 두 가지 정적 메소드는 다음과 같이 구현할 수 있습니다 (선택적으로 정적 가져 오기와 함께 사용).

private static <T,A,R,S> Collector<T,?,S> combine(Collector<T,A,R> collector, Function<? super R, ? extends S> function) {
    return Collector.of(
        collector.supplier(),
        collector.accumulator(),
        collector.combiner(),
        collector.finisher().andThen(function));
}
public static <T> Collector<T,?,Stream<T>> concat(Stream<? extends T> other) {
    return combine(Collectors.toList(),
        list -> Stream.concat(list.stream(), other));
}
public static <T> Collector<T,?,Stream<T>> concat(T element) {
    return concat(Stream.of(element));
}

물론이 솔루션에는 언급해야 할 단점이 있습니다. 수집 은 스트림의 모든 요소를 ​​사용하는 최종 작업입니다. 또한 콜렉터 concat 은 체인에서 사용될 때마다 중간 ArrayList를 만듭니다 . 두 작업 모두 프로그램 동작에 큰 영향을 줄 수 있습니다. 그러나 가독성성능 보다 중요한 경우 에도 여전히 유용한 방법 일 수 있습니다.


답변

불행히도이 답변은 거의 도움이되지 않을 수도 있지만 Java Lambda 메일 링리스트의 법의학 분석을 수행 하여이 디자인의 원인을 찾을 수 있는지 확인했습니다. 이것이 내가 알게 된 것입니다.

처음에는 Stream.concat (Stream)에 대한 인스턴스 메소드가있었습니다.

메일 링리스트 에서이 메소드에서 Paul Sandoz가 concat 작업에 대해 읽을 수있는 것처럼 메소드가 원래 인스턴스 메소드로 구현 된 것을 알 수 있습니다 .

이 글에서는 스트림이 무한 할 수있는 경우와 그 연결에서 어떤 의미가 있을지에 대해 발생할 수있는 문제에 대해 논의하지만, 이것이 수정 이유라고 생각하지 않습니다.

이 다른 스레드 에서 JDK 8의 일부 초기 사용자가 널 인수와 함께 사용될 때 concat 인스턴스 메소드의 동작에 대해 의문을 보았습니다 .

그러나이 다른 스레드 는 concat 메소드의 설계가 논의 중임을 밝힙니다.

Streams.concat (Stream, Stream)로 리팩토링

그러나 아무런 설명없이 갑자기이 스레드 에서 스트림 결합에 대해 볼 수 있듯이 메소드가 정적 메소드로 변경되었습니다 . 이것은 아마도이 변경에 대해 약간의 빛을 발하는 유일한 메일 스레드 일지 모르지만 리팩토링의 이유를 결정할만큼 명확하지 않았습니다. 그러나 우리는 그들이 메소드를 헬퍼 클래스 밖으로 이동하도록 제안 하는 커밋 을 보았습니다 .concatStreamStreams

Stream.concat (Stream, Stream)로 리팩토링

나중에 그것을 다시 이동되지 않은 에서 StreamsStream있지만 아직 다시 아무런 설명.

결론적으로 디자인의 이유는 완전히 명확하지 않으며 좋은 설명을 찾을 수 없습니다. 여전히 메일 링리스트에서 질문을 할 수있을 것 같습니다.

스트림 연결을위한 몇 가지 대안

Michael Hixson 의이 다른 스레드는 스트림을 결합 / 연결하는 다른 방법에 대해 설명하고 묻습니다.

  1. 두 개의 스트림을 결합하려면 다음을 수행해야합니다.

    Stream.concat(s1, s2)

    이거 말고:

    Stream.of(s1, s2).flatMap(x -> x)

    … 권리?

  2. 두 개 이상의 스트림을 결합하려면 다음을 수행해야합니다.

    Stream.of(s1, s2, s3, ...).flatMap(x -> x)

    이거 말고:

    Stream.of(s1, s2, s3, ...).reduce(Stream.empty(), Stream::concat)

    … 권리?


답변

StreamEx 라이브러리는 Stream API의 기능을 확장합니다. 특히이 문제를 해결하는 appendprepend 와 같은 메소드를 제공합니다 (내부적으로 사용 concat). 이러한 메소드는 다른 스트림 또는 콜렉션 또는 varargs 배열을 승인 할 수 있습니다. 내 라이브러리를 사용하면 문제를 다음과 같이 해결할 수 있습니다 ( x != 0기본이 아닌 스트림에서는 이상하게 보입니다).

Stream<Integer> stream = StreamEx.of(stream1)
             .filter(x -> !x.equals(0))
             .append(stream2)
             .filter(x -> !x.equals(1))
             .append(element)
             .filter(x -> !x.equals(2));

그런데 filter작업에 대한 바로 가기도 있습니다.

Stream<Integer> stream = StreamEx.of(stream1).without(0)
                                 .append(stream2).without(1)
                                 .append(element).without(2);


답변

그냥 해:

Stream.of(stream1, stream2, Stream.of(element)).flatMap(identity());

identity()의 정적 가져 오기는 어디 입니까 Function.identity()?

여러 스트림을 하나의 스트림으로 연결하는 것은 스트림을 병합하는 것과 같습니다.

그러나 불행히도 어떤 이유로 든에 flatten()메소드 가 없으므로 identity 함수와 함께 Stream사용해야 flatMap()합니다.


답변

정적 가져 오기에서는 매우 짧은 Guava의 방법을 사용할 수 있습니다 .Streams.concat(Stream<? extends T>... streams)

Stream stream = concat(stream1, stream2, of(element));


답변

타사 라이브러리를 사용하는 것이 마음에 들지 않으면 cyclops-react 에는 추가 / 접두사 연산자를 통해 확장 스트림 유형을 사용할 수 있습니다.

개별 값, 배열, 반복 가능 항목, 스트림 또는 반응 스트림 게시자를 인스턴스 메소드로 추가하고 추가 할 수 있습니다.

Stream stream = ReactiveSeq.of(1,2)
                           .filter(x -> x!=0)
                           .append(ReactiveSeq.of(3,4))
                           .filter(x -> x!=1)
                           .append(5)
                           .filter(x -> x!=2);

[공개 나는 사이클롭스 반응의 주요 개발자입니다]


답변

하루가 끝나면 스트림 결합에 관심이 없지만 모든 스트림의 각 요소를 처리 한 결과를 얻는 데 관심이 있습니다.

스트림을 결합하는 것은 번거로울 수 있지만 (이 스레드) 처리 결과를 결합하는 것은 매우 쉽습니다.

해결의 열쇠는 자신의 컬렉터를 생성하고 새로운 컬렉터의 공급 함수가 반환하도록하는 것입니다 같은 때마다 (컬렉션 으로 새 사람이 ), 아래 코드는이 방법을 설명합니다.

package scratchpad;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collector;
import java.util.stream.Stream;

public class CombineStreams {
    public CombineStreams() {
        super();
    }

    public static void main(String[] args) {
        List<String> resultList = new ArrayList<>();
        Collector<String, List<String>, List<String>> collector = Collector.of(
                () -> resultList,
                (list, item) -> {
                    list.add(item);
                },
                (llist, rlist) -> {
                    llist.addAll(rlist);
                    return llist;
                }
        );
        String searchString = "Wil";

        System.out.println("After processing first stream\n"
                + createFirstStream().filter(name -> name.contains(searchString)).collect(collector));
        System.out.println();

        System.out.println("After processing second stream\n"
                + createSecondStream().filter(name -> name.contains(searchString)).collect(collector));
        System.out.println();

        System.out.println("After processing third stream\n"
                + createThirdStream().filter(name -> name.contains(searchString)).collect(collector));
        System.out.println();

    }

    private static Stream<String> createFirstStream() {
        return Arrays.asList(
                "William Shakespeare",
                "Emily Dickinson",
                "H. P. Lovecraft",
                "Arthur Conan Doyle",
                "Leo Tolstoy",
                "Edgar Allan Poe",
                "Robert Ervin Howard",
                "Rabindranath Tagore",
                "Rudyard Kipling",
                "Seneca",
                "John Donne",
                "Sarah Williams",
                "Oscar Wilde",
                "Catullus",
                "Alfred Tennyson",
                "William Blake",
                "Charles Dickens",
                "John Keats",
                "Theodor Herzl"
        ).stream();
    }

    private static Stream<String> createSecondStream() {
        return Arrays.asList(
                "Percy Bysshe Shelley",
                "Ernest Hemingway",
                "Barack Obama",
                "Anton Chekhov",
                "Henry Wadsworth Longfellow",
                "Arthur Schopenhauer",
                "Jacob De Haas",
                "George Gordon Byron",
                "Jack London",
                "Robert Frost",
                "Abraham Lincoln",
                "O. Henry",
                "Ovid",
                "Robert Louis Stevenson",
                "John Masefield",
                "James Joyce",
                "Clark Ashton Smith",
                "Aristotle",
                "William Wordsworth",
                "Jane Austen"
        ).stream();
    }

    private static Stream<String> createThirdStream() {
        return Arrays.asList(
                "Niccolò Machiavelli",
                "Lewis Carroll",
                "Robert Burns",
                "Edgar Rice Burroughs",
                "Plato",
                "John Milton",
                "Ralph Waldo Emerson",
                "Margaret Thatcher",
                "Sylvie d'Avigdor",
                "Marcus Tullius Cicero",
                "Banjo Paterson",
                "Woodrow Wilson",
                "Walt Whitman",
                "Theodore Roosevelt",
                "Agatha Christie",
                "Ambrose Bierce",
                "Nikola Tesla",
                "Franz Kafka"
        ).stream();
    }
}