IT이야기

2개의 Java 8 스트림 또는 스트림에 추가 요소 추가

cyworld 2022. 5. 27. 21:23
반응형

2개의 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 if ifconcat인스턴스 방식이라면 위의 예를 훨씬 쉽게 읽을 수 있을 것입니다.

 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) 어떤 경우에도 더 좋은 방법이 있을까요?

유감스럽게도 이 답변은 전혀 도움이 되지 않을 것입니다만, 저는 이 디자인의 원인을 찾기 위해 Java Lambda Mailing 목록을 법의학적으로 분석했습니다.이게 내가 알아낸 거야

처음에 Stream.concat(Stream) 인스턴스 메서드가 있었습니다.

메일링 리스트를 보면, 이 메서드는 원래 인스턴스 방식으로서 실장되어 있는 것을 알 수 있습니다.이 스레드에서 볼 수 있듯이 Concat 조작에 대해 Paul Sandoz가 읽고 있습니다.

그 안에서 스트림이 무한할 수 있는 경우에서 발생할 수 있는 문제나 그 경우에서의 연계가 무엇을 의미하는지 논의하지만, 그것이 수정의 이유였다고는 생각하지 않는다.

다른 스레드에서는 JDK 8의 일부 초기 사용자가 null 인수와 함께 사용할 때의 Concat 인스턴스 메서드의 동작에 대해 질문한 것을 알 수 있습니다.

그러나다른 맥락은 콘캣 방법의 설계가 논의 중이었음을 보여준다.

Streams.concat(Stream, Stream)으로 리팩터링됨

그러나 설명 없이 갑자기 스트림을 조합하는 방법에 대해스레드에서 볼 수 있듯이 메서드가 정적 메서드로 변경되었습니다.이 변경에 대해 조금이나마 알 수 있는 메일 스레드는 이것뿐이지만, 리팩터링의 이유를 알 수 있을 만큼 명확하지 않았습니다.하지만 우리는 그들이 제안했던 을 볼 수 있습니다.concatStream 클래스 「」에 .Streams

Stream.concat(Stream, Stream)으로 리팩터링됨

나중에 다시 옮겨졌다.Streams로로 합니다.Stream하지만, 다시 말하지만, 그것에 대한 설명은 없습니다.

그래서 결론부터 말하자면, 디자인의 이유가 완전히 명확하지 않고, 좋은 설명을 찾을 수 없었습니다.메일링 리스트에 있는 질문도 할 수 있을 것 같습니다.

스트림 연결을 위한 몇 가지 대체 방법

Michael Hixson의 다른 스레드에서는 스트림을 결합/접합하는 다른 방법에 대해 설명/질문합니다.

  1. 2개의 스트림을 결합하려면 다음과 같이 해야 합니다.

    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)
    

    ...그렇죠?

Stream.concat 및 Stream.of정적 Import를 추가하면 첫 번째 예가 다음과 같이 작성될 수 있습니다.

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

이러한 2개의 스태틱 방식(옵션으로 스태틱 Import와 조합)을 사용하면, 2개의 예를 다음과 같이 기술할 수 있습니다.

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

이제 코드가 상당히 짧아졌습니다.하지만 가독성이 향상되지 않았다는 것에 동의합니다.그래서 다른 해결책이 있어요.


대부분의 경우 Collector사용하여 스트림의 기능을 확장할 수 있습니다.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(...)로 대체해야 한다는 것입니다.다음의 2개의 스태틱 방식을 실장할 수 있습니다(옵션으로 스태틱 Import와 조합해 사용합니다).

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

물론 이 솔루션에는 언급해야 할 단점이 있습니다.collect는 스트림의 모든 요소를 소비하는 최종 작업입니다.또한 Collector Concat는 체인으로 사용될 때마다 중간 ArrayList를 작성합니다.두 작업 모두 프로그램 동작에 큰 영향을 미칠 수 있습니다.그러나 성능보다 가독성이 더 중요한 경우에는 여전히 매우 유용한 접근법이 될 수 있습니다.

다음 작업을 수행합니다.

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

서 ''는identity()는 Import하다의 스태틱 입니다.Function.identity().

여러 스트림을 하나의 스트림에 연결하는 것은 스트림을 평평하게 만드는 것과 같습니다.

도, 어떤 로, 「」, 「」, 「」는 없습니다.flatten()의 메서드입니다.Stream 써야 flatMap()이치노

My StreamEx 라이브러리는 Stream API의 기능을 확장합니다.특히 이 문제를 해결하는 append 및 prepend 등의 방법을 제공합니다(내부에서는 이 방법을 사용합니다).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);

Guava's를 사용할 수 있습니다..concat(Stream<? extends T>... streams) method: 평탄한 스트림을 생성합니다.

Stream stream = Streams.concat(stream1, stream2, Stream.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();
    }
}

당신만의 콘캣 방법을 써보는 건 어때요?

public static <T> Stream<T> concat(Stream<? extends T> a, 
                                   Stream<? extends T> b, 
                                   Stream<? extends T>... args)
{
    Stream<T> concatenated = Stream.concat(a, b);
    for (Stream<? extends T> stream : args)
    {
        concatenated = Stream.concat(concatenated, stream);
    }
    return concatenated;
}

적어도 첫 번째 예시는 훨씬 읽기 쉬워집니다.

@Legna가 지적한 바와 같이 Stream::concat에 대한 콜이 중첩되어 StackOverflowError가 발생할 가능성이 있습니다.

이 문제를 해결할 수 있는 다른 버전을 다음에 제시하겠습니다.

public static <T> Stream<T> concat(final Stream<? extends T>... args)
{
    return args == null ? Stream.empty()
                        : Stream.of(args).flatMap(Function.identity());
}

언급URL : https://stackoverflow.com/questions/22740464/adding-two-java-8-streams-or-an-extra-element-to-a-stream

반응형