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


168

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

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) 어쨌든 더 좋은 방법이 있습니까?


4
것 같습니다 것은 항상 이런했다 ,하지만 난 그냥 이유를 찾을 수 없습니다.
Edwin Dalorzo

답변:


126

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를 만듭니다 . 두 작업 모두 프로그램 동작에 큰 영향을 줄 수 있습니다. 그러나 가독성성능 보다 중요한 경우 에도 여전히 유용한 방법 일 수 있습니다.


1
concat수집기가 많이 읽히지 않습니다 . 이와 같은 단일 매개 변수 정적 메서드가 있고 collect연결 에도 사용하는 것이 이상하게 보입니다 .
Didier L

@nosid, 아마도이 스레드에 약간 직교하는 질문이지만 왜 주장 It's a bad idea to import static methods with names합니까? 나는 정말로 관심이 있습니다-코드가 더 간결하고 읽기 쉬우 며 많은 사람들이 같은 생각을한다는 것을 알았습니다. 왜 이것이 일반적으로 나쁜지에 대한 몇 가지 예를 제공하려고주의하십니까?
양자

1
@Quantum : 무슨 의미 compare(reverse(getType(42)), of(6 * 9).hashCode())입니까? 정적 가져 오기 는 좋지 않은 아이디어 라고 말하지 않았고 , 일반 이름에 대한 정적 가져 오기ofand concat입니다.
nosid December

1
@nosid : 최신 IDE에서 각 문장 위로 마우스를 가져 가면 의미가 빨리 드러나지 않습니까? 어쨌든, "일반적인"이름에 대한 정적 임포트가 나쁜 이유를 아직 알지 못하기 때문에 이것이 개인적으로 선호하는 주장이라고 생각합니다. 어떤 경우에 메모장이나 VI (M)을 사용하지 않는다면 더 큰 문제가 있습니다.
quantum

스칼라 SDK가 더 좋다고 말하지는 않겠지 만 ..
eirirlar

165

불행히도이 답변은 거의 도움이되지 않을 수도 있지만 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)

    ... 권리?


6
+1 멋진 연구. 그리고 나는 이것을 var.gs를 취하는 나의 Stream.concat로 사용할 것이다 :public static <T> Stream<T> concat(Stream<T>... streams) { return Stream.of(streams).reduce(Stream.empty(), Stream::concat);}
MarcG

1
오늘 나는 내 자신의 concat 버전을 썼다. 서명은 약간 다르지만 더 일반적이기 때문에 예를 들어 Stream <Integer>와 Stream <Double>을 Stream <Number>에 병합 할 수 있습니다. @SafeVarargs private static <T> Stream<T> concat(Stream<? extends T>... streams) { return Stream.of(streams).reduce(Stream.empty(),Stream::concat).map(Function.identity());}
칸트

@kant 왜 Function.identity()지도 가 필요한 가요? 결국, 그것은 동일한 인수를 반환합니다. 결과 스트림에는 영향을 미치지 않습니다. 뭔가 빠졌습니까?
Edwin Dalorzo

1
IDE에 입력하려고 했습니까? .map (identity ())가 없으면 컴파일 오류가 발생합니다. Stream <T>를 반환하고 싶지만 Statement : return Stream.of(streams).reduce(Stream.empty(),Stream::concat)returns Stream <? T>를 확장합니다. (Someting <T>는 Something <? extends T>의 다른 유형이 아니므로 캐스트 할 수 없었습니다.) 추가 .map(identity())캐스트 <? T>를 <T>로 확장합니다. 메소드 인수의 Java 8 '대상 유형'과 리턴 유형 및 map () 메소드의 서명으로 인해 발생합니다. 실제로는 Function. <T> identity ()입니다.
칸트

1
@kant 캡처 변환을? extends T 사용할 수 있기 때문에 별로 중요하지 않습니다 . 어쨌든 여기 요점 코드 스 니펫이 있습니다 요점 에서 계속 논의하겠습니다.
Edwin Dalorzo

12

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

9

그냥 해:

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

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

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

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



1

타사 라이브러리를 사용하는 것이 마음에 들지 않으면 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);

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


1

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

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

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

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

0

concat 메소드를 작성하는 것은 어떻습니까?

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

이것은 적어도 첫 번째 예제를 훨씬 더 읽기 쉽게 만듭니다.


1
반복 연결에서 스트림을 구성 할 때는주의하십시오. 깊이 연결된 스트림의 요소에 액세스하면 딥 콜 체인 또는 심지어 StackOverflowError가 발생할 수 있습니다.
Legna
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.