Mam zestaw danych reprezentowany przez strumień Java 8:
Stream<T> stream = ...;
Widzę, jak to przefiltrować, aby uzyskać losowy podzbiór - na przykład
Random r = new Random();
PrimitiveIterator.OfInt coin = r.ints(0, 2).iterator();
Stream<T> heads = stream.filter((x) -> (coin.nextInt() == 0));
Widzę również, jak mogę zredukować ten strumień, aby uzyskać na przykład dwie listy reprezentujące dwie losowe połowy zestawu danych, a następnie przekształcić je z powrotem w strumienie. Ale czy istnieje bezpośredni sposób na wygenerowanie dwóch strumieni z pierwszego? Coś jak
(heads, tails) = stream.[some kind of split based on filter]
Dzięki za wgląd.
java
java-8
java-stream
user1148758
źródło
źródło
Stream
na wieleStream
s bez konwersji pośredniej , chociaż myślę, że ludzie, którzy dotarli do tego pytania, faktycznie szukają sposobu, aby to osiągnąć, niezależnie od takiego ograniczenia, co jest odpowiedzią Marka. Może to wynikać z faktu, że pytanie w tytule nie jest takie samo jak w opisie .Odpowiedzi:
Nie dokładnie. Nie możesz wyciągnąć dwóch
Stream
z jednego; to nie ma sensu - jak można iterować po jednym bez konieczności generowania drugiego w tym samym czasie? Strumień można obsługiwać tylko raz.Jeśli jednak chcesz zrzucić je na listę lub coś, możesz to zrobić
źródło
stream.collect(...)
for ze wstępnie zdefiniowanym bezpiecznym wątkiemCollectors
, który działa dobrze nawet na kolekcjach bez zabezpieczeń wątków (bez synchronicznej rywalizacji o blokady). Najlepsza odpowiedź od @MarkJeronimus.Kolektor może być używany do tego celu.
Collectors.partitioningBy()
factory.Spowoduje to utworzenie
Map
odBoolean
doList
i umieszczenie elementów na jednej lub drugiej liście w oparciu oPredicate
.Uwaga: ponieważ strumień musi być zużyty w całości, nie może to działać w przypadku nieskończonych strumieni. A ponieważ strumień i tak jest zużyty, ta metoda po prostu umieszcza je na listach zamiast tworzyć nowy strumień z pamięcią. Zawsze możesz przesyłać strumieniowo te listy, jeśli potrzebujesz strumieni jako danych wyjściowych.
Nie ma też potrzeby stosowania iteratora, nawet w podanym przykładzie obejmującym tylko głowy.
Collectors.groupingBy()
fabryki.W przypadku, gdy strumienie nie są
Stream
, ale są jednym z strumieni pierwotnych, takich jakIntStream
, to ta.collect(Collectors)
metoda nie jest dostępna. Będziesz musiał to zrobić ręcznie bez fabryki kolektorów. Jego implementacja wygląda następująco:[Przykład 2.0 od 16.04.2020]
W tym przykładzie inicjalizuję ArrayLists pełnym rozmiarem początkowej kolekcji (jeśli w ogóle jest to znane). Zapobiega to zdarzeniom zmiany rozmiaru nawet w najgorszym przypadku, ale może potencjalnie pochłonąć 2 * N * T przestrzeni (N = początkowa liczba elementów, T = liczba wątków). Aby poświęcić miejsce na szybkość, możesz to pominąć lub wykorzystać swoje najlepsze przypuszczenia, takie jak oczekiwana najwyższa liczba elementów w jednej partycji (zwykle nieco ponad N / 2 dla zrównoważonego podziału).
Mam nadzieję, że nikogo nie obrażam, używając metody Java 9. W przypadku wersji Java 8 spójrz na historię edycji.
źródło
stream.boxed().collect(...);
! Zrobi to, co reklamowano: przekonwertuj prymitywIntStream
naStream<Integer>
wersję pudełkową .(map, x) -> { boolean partition = p.test(x); List<Integer> list = map.get(partition); list.add(x); }
możesz po prostu użyć(map, x) -> map.get(p.test(x)).add(x)
. Ponadto nie widzę żadnego powodu, dla któregocollect
operacja nie powinna być bezpieczna dla wątków. Działa dokładnie tak, jak ma działać i bardzo blisko tegoCollectors.partitioningBy(p)
, jak miałoby działać. Ale użyłbymIntPredicate
zamiast tego,Predicate<Integer>
kiedy nie używamboxed()
, aby uniknąć podwójnego boksowania.Natknąłem się na to pytanie i czuję, że rozwidlony strumień ma kilka przypadków użycia, które mogą okazać się słuszne. Napisałem poniższy kod jako konsument, aby nic nie robił, ale możesz go zastosować do funkcji i wszystkiego, co możesz napotkać.
Teraz Twoja implementacja kodu może wyglądać mniej więcej tak:
źródło
Niestety, to, o co prosisz, jest bezpośrednio źle widziane w JavaDoc of Stream :
Możesz obejść to za pomocą
peek
lub innych metod, jeśli naprawdę pragniesz tego typu zachowania. W takim przypadku zamiast próbować cofnąć dwa strumienie z tego samego oryginalnego źródła strumienia za pomocą filtru rozwidlającego, należy powielić strumień i odpowiednio przefiltrować każdy z duplikatów.Możesz jednak zechcieć ponownie rozważyć, czy struktura
Stream
jest odpowiednia dla twojego przypadku użycia.źródło
List<Stream> forkStream(Stream s)
ale moje otrzymane strumienie będą przynajmniej częściowo wspierane przez kolekcje, a nie bezpośrednio przez strumień bazowy, w przeciwieństwie do tego,filter
co nie jest operacją na strumieniu terminala.Jest to sprzeczne z ogólnym mechanizmem Stream. Powiedzmy, że możesz podzielić strumień S0 na Sa i Sb tak, jak chcesz. Wykonanie dowolnej operacji terminalowej, powiedzmy
count()
, na Sa z konieczności „zużyje” wszystkie elementy w S0. Dlatego Sb stracił źródło danych.Wydaje mi się, że wcześniej Stream miał
tee()
metodę, która kopiowała strumień do dwóch. Jest teraz usunięty.Stream ma jednak metodę peek (), możesz jej użyć do spełnienia swoich wymagań.
źródło
peek
jest dokładnie tym, czym było kiedyśtee
.niezupełnie, ale możesz być w stanie osiągnąć to, czego potrzebujesz, przywołując
Collectors.groupingBy()
. tworzysz nową kolekcję, a następnie możesz utworzyć instancje strumieni w tej nowej kolekcji.źródło
To była najmniej zła odpowiedź, jaką mogłem wymyślić.
To pobiera strumień liczb całkowitych i dzieli je na 5. Dla tych większych niż 5 filtruje tylko liczby parzyste i umieszcza je na liście. Reszta łączy je z |.
wyjścia:
Nie jest idealny, ponieważ gromadzi wszystko w kolekcjach pośrednich, przerywając strumień (i ma zbyt wiele argumentów!)
źródło
Natknąłem się na to pytanie, szukając sposobu na odfiltrowanie pewnych elementów ze strumienia i zarejestrowanie ich jako błędów. Więc tak naprawdę nie musiałem tak bardzo dzielić strumienia, ile dołączyć przedwczesną akcję kończącą do predykatu z dyskretną składnią. Oto, co wymyśliłem:
źródło
Krótsza wersja wykorzystująca Lombok
źródło
Co powiesz na:
źródło