簡體   English   中英

向流中添加兩個 Java 8 流或一個額外的元素

[英]Adding two Java 8 streams, or an extra element to a stream

我可以添加流或額外元素,如下所示:

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)無論如何,有沒有更好的方法來做到這一點?

不幸的是,這個答案可能沒有任何幫助,但我對 Java Lambda 郵件列表進行了取證分析,看看我是否能找到這種設計的原因。 這是我發現的。

一開始有 Stream.concat(Stream) 的實例方法

在郵件列表中,我可以清楚地看到該方法最初是作為實例方法實現的,您可以在 Paul Sandoz 的這個線程中閱讀有關 concat 操作的內容。

在其中,他們討論了流可能是無限的情況下可能出現的問題以及在這些情況下連接意味着什么,但我認為這不是修改的原因。

您在另一個線程中看到,一些 JDK 8 的早期用戶質疑 concat 實例方法在與空參數一起使用時的行為。

然而, 另一個線程表明 concat 方法的設計正在討論中。

重構為 Streams.concat(Stream,Stream)

但是沒有任何解釋,突然,這些方法更改為靜態方法,正如您在有關組合流的線程中所見。 這可能是唯一對這一變化有所了解的郵件線程,但我還不夠清楚,無法確定重構的原因。 但是我們可以看到他們做了一個提交,他們建議將concat方法從Stream移到幫助類Streams

重構為 Stream.concat(Stream,Stream)

后來, 它再次Streams移到Stream ,但又一次,沒有解釋。

所以,歸根結底,設計的原因對我來說並不完全清楚,我找不到很好的解釋。 我想你仍然可以在郵件列表中提出這個問題。

流連接的一些替代方案

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)

    ... 對?

如果你為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));
}

當然,這個解決方案有一個缺點,應該提到。 collect是消耗流中所有元素的最終操作。 最重要的是,收集器concat每次在鏈中使用時都會創建一個中間ArrayList 這兩種操作都會對程序的行為產生重大影響。 然而,如果可讀性性能更重要,它可能仍然是一個非常有用的方法。

我的StreamEx庫擴展了 Stream API 的功能。 特別是它提供了appendprepend 之類的方法來解決這個問題(在內部他們使用concat )。 這些方法可以接受另一個流或集合或可變參數數組。 使用我的庫,您的問題可以通過這種方式解決(請注意,對於非原始流, 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()的靜態導入。

將多個流連接成一個流與展平流相同。

但是,不幸的是,由於某種原因, Stream上沒有flatten()方法,因此您必須將flatMap()與標識函數一起使用。

您可以使用 Guava 的Streams . concat(Stream<? extends T>... streams)方法,這會導致扁平化的流:

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

如果您不介意使用 3rd Party Libraries,那么cyclops-react有一個擴展的 Stream 類型,它允許您通過 append / prepend 運算符來做到這一點。

單個值、數組、可迭代對象、流或反應流發布者可以作為實例方法附加和前置。

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

[披露我是獨眼巨人反應的首席開發人員]

編寫自己的 concat 方法怎么樣?

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

歸根結底,我對組合流不感興趣,但對獲得處理所有這些流的每個元素的組合結果不感興趣。

雖然合並流可能被證明是麻煩的(因此這個線程),合並它們的處理結果是相當容易的。

解決的關鍵是創建自己的收集器並確保新收集器的供應商函數每次返回相同的集合(不是新的),下面的代碼說明了這種方法。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM