Dodanie dwóch strumieni Java 8 lub dodatkowego elementu do strumienia

Mogę dodawać strumienie lub dodatkowe elementy, takie jak:

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

I mogę dodawać nowe rzeczy, jak to:

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

Ale to jest brzydkie, ponieważ concat jest statyczne. Gdyby concat były metodą instancji, powyższe przykłady byłyby znacznie łatwiejsze do odczytania:

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

I

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

Moje pytanie brzmi:

1) Czy Jest jakiś dobry powód, dla którego concat jest statyczny? A może brakuje mi jakiejś równoważnej metody instancji?

2) w każdym razie, czy jest lepszy jak to zrobić?

Author: skiwi, 2014-03-30

8 answers

Jeśli dodasz statyczny IMPORT dla strumienia.concat i Stream.z pierwszy przykład można zapisać w następujący sposób:

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

Importowanie metod statycznych z nazwami rodzajowymi może spowodować powstanie kodu, który staje się trudny do odczytania i utrzymania (przestrzeń nazw). Więc może lepiej będzie stworzyć własne statyczne metody z bardziej znaczącymi nazwami. Jednak dla demonstracji będę trzymał się tej nazwy.

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

Z tymi dwoma metody statyczne (opcjonalnie w połączeniu z importem statycznym), dwa przykłady można zapisać w następujący sposób:

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

Kod jest teraz znacznie krótszy. Zgadzam się jednak, że czytelność się nie poprawiła. Więc mam inne rozwiązanie.


W wielu sytuacjach, Kolektory mogą być używane do rozszerzenia funkcjonalności strumieni. Z dwoma kolorami na dole, dwa przykłady można zapisać w następujący sposób:

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

The jedyną różnicą między żądaną składnią a powyższą jest to, że musisz zastąpić concat(...) z collect(concat(...)). Dwie statyczne metody mogą być zaimplementowane w następujący sposób (opcjonalnie stosowane w połączeniu z importem statycznym):

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

Oczywiście jest pewna wada tego rozwiązania, o której należy wspomnieć. collect jest operacją końcową, która pochłania wszystkie elementy strumienia. Dodatkowo kolektor concat tworzy pośredni ArrayList za każdym razem, gdy jest używany w łańcuchu. Obie operacje mogą mieć znaczący wpływ na zachowanie programu. Jeśli jednak czytelność jest ważniejsza niż wydajność {7]}, może to być bardzo pomocne podejście.

 127
Author: nosid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-30 19:04:21

Niestety ta odpowiedź jest prawdopodobnie mało pomocna lub wcale, ale zrobiłem analizę kryminalistyczną listy dyskusyjnej Java Lambda, aby sprawdzić, czy mogę znaleźć przyczynę tego projektu. Tego się dowiedziałem.

Na początku istniała metoda instancyjna dla Stream.concat (Stream)

Na liście dyskusyjnej wyraźnie widzę, że metoda została pierwotnie zaimplementowana jako metoda instancyjna, co można przeczytać w w tym wątku Paula Sandoza, o concat operacja.

W nim omawiają kwestie, które mogą wyniknąć z tych przypadków, w których strumień może być nieskończony i co oznacza konkatenacja w tych przypadkach, ale nie sądzę, że był to powód modyfikacji.

Widzisz w ten drugi wątek , że niektórzy pierwsi użytkownicy JDK 8 pytali o zachowanie metody instancji concat, gdy jest używana z argumentami null.

Ten inny wątek ujawnia jednak, że konstrukcja metoda concat była przedmiotem dyskusji.

Refakturowane do strumieni.concat (strumień,strumień)

Ale bez żadnego wyjaśnienia, nagle, metody zostały zmienione na metody statyczne, jak widać w ten wątek o łączeniu strumieni. Jest to chyba jedyny wątek pocztowy, który rzuca trochę światła na temat tej zmiany, ale nie było wystarczająco jasne dla mnie, aby określić powód refaktoryzacji. Ale widzimy, że zrobili commit , w którym zasugerowali aby przenieść metodę concat z Stream i do klasy pomocniczej Streams.

Refakturowany do strumienia.concat (strumień,strumień)

Później, został przeniesiony ponownie z Streams do Stream, ale po raz kolejny, nie ma na to wyjaśnienia.

Podsumowując, powód projektu nie jest dla mnie do końca jasny i nie mogłem znaleźć dobrego wyjaśnienia. Myślę, że nadal możesz zadać pytanie na liście mailingowej.

Kilka alternatyw dla Stream Konkatenacja

This other thread by Michael Hixson omawia / pyta o inne sposoby łączenia / łączenia strumieni

  1. Aby połączyć dwa strumienie, powinienem zrobić to:

    Stream.concat(s1, s2)
    

    Nie to:

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

    ... prawda?

  2. Aby połączyć więcej niż dwa strumienie, powinienem zrobić to:

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

    Nie to:

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

    ... prawda?

 166
Author: Edwin Dalorzo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-01 17:06:20

My StreamEx biblioteka rozszerza funkcjonalność Stream API. W szczególności oferuje metody takie jak append i prepend , które rozwiązują ten problem (wewnętrznie używają concat). Metody te mogą przyjmować inną tablicę stream lub collection lub varargs. Korzystając z mojej biblioteki twój problem można rozwiązać w ten sposób (zauważ, że x != 0 wygląda dziwnie dla nie-prymitywnego strumienia):

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

Przy okazji jest też skrót do twojej operacji filter:

Stream<Integer> stream = StreamEx.of(stream1).without(0)
                                 .append(stream2).without(1)
                                 .append(element).without(2);
 12
Author: Tagir Valeev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-11 12:31:19

Po prostu zrób:

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

Gdzie identity() jest statycznym importem Function.identity().

Połączenie wielu strumieni w jeden strumień jest tym samym, co spłaszczenie strumienia.

Jednak, niestety, z jakiegoś powodu nie ma flatten() metody na Stream, więc musisz użyć flatMap() z funkcją tożsamościową.

 10
Author: herman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-30 14:06:47

Możesz użyć Guava ' S Streams.concat(Stream<? extends T>... streams) metoda, która będzie bardzo krótka przy imporcie statycznym:

Stream stream = concat(stream1, stream2, of(element));
 2
Author: Kunda,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-25 02:11:04

Jeśli nie masz nic przeciwko użyciu bibliotek zewnętrznych cyclops-react ma rozszerzony Typ strumienia, który pozwoli Ci to zrobić za pomocą operatorów append / prepend.

Poszczególne wartości, tablice, iteraby, strumienie lub reactive-streams mogą być dodawane i poprzedzane jako metody instancji.

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

[Disclosure I am the lead developer of cyclops-react]

 1
Author: John McClean,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-10 15:48:17

Może napiszesz własną metodę 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;
}

To przynajmniej sprawia, że twój pierwszy przykład jest o wiele bardziej czytelny.

 0
Author: Felix S,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-21 11:31:34

Na koniec dnia nie interesuje mnie łączenie strumieni, ale uzyskanie połączonego wyniku przetwarzania każdego elementu wszystkich tych strumieni.

Podczas gdy łączenie strumieni może okazać się kłopotliwe( tak więc ten wątek), łączenie ich wyników przetwarzania jest dość łatwe.

Kluczem do rozwiązania jest stworzenie własnego kolektora i upewnienie się, że funkcja dostawcy dla nowego kolektora zwraca za każdym razem tę samą kolekcję (nie nową ), poniższy kod ilustruje to podejście.

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
Author: Legna,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-12 18:53:58