Biorąc pod uwagę strumień, taki jak { 0, 1, 2, 3, 4 }
,
jak mogę najbardziej elegancko nadać mu daną formę:
{ new Pair(0, 1), new Pair(1, 2), new Pair(2, 3), new Pair(3, 4) }
(zakładając oczywiście, że zdefiniowałem parę klas)?
Edycja: nie dotyczy to wyłącznie ints ani strumieni pierwotnych. Odpowiedź powinna być ogólna dla dowolnego typu strumienia.
java
java-8
java-stream
Aleksandr Dubinsky
źródło
źródło
list.stream().map(i -> new Pair(i, i+1));
Map.Entry
klasy pary. (To prawda, niektórzy mogą uważać, że hack, ale użycie wbudowanej klasy jest przydatne).Odpowiedzi:
Moja biblioteka StreamEx, która rozszerza standardowe strumienie, zapewnia
pairMap
metodę dla wszystkich typów strumieni. W przypadku strumieni pierwotnych nie zmienia typu strumienia, ale może służyć do wykonywania pewnych obliczeń. Najczęstszym zastosowaniem jest obliczanie różnic:Dla strumienia obiektów można utworzyć dowolny inny typ obiektu. Moja biblioteka nie dostarcza żadnych nowych struktur danych widocznych dla użytkownika, takich jak
Pair
(to jest część koncepcji biblioteki). Jeśli jednak masz własnąPair
klasę i chcesz z niej korzystać, możesz wykonać następujące czynności:Lub jeśli już masz
Stream
:Ta funkcja jest realizowana przy użyciu niestandardowego rozdzielacza . Ma dość niski narzut i może ładnie pracować równolegle. Oczywiście działa z każdym źródłem strumienia, a nie tylko z listami / tablicami o swobodnym dostępie, jak wiele innych rozwiązań. W wielu testach spisuje się naprawdę dobrze. Oto test porównawczy JMH, w którym wszystkie wartości wejściowe poprzedzające większą wartość przy użyciu różnych podejść (zobacz to pytanie).
źródło
StreamEx
realizujeIterable
! Hurra!)Stream
wStreamEx
?StreamEx.of(stream)
. Istnieją inne wygodne statyczne metody tworzenia strumieniaCollection
, tablicReader
itp. Edytowano odpowiedź.pairMap
zamawiany w sekwencyjnych strumieniach? Właściwie chciałbym mieć forPairsOrdered (), ale skoro nie ma takiej metody, czy mogę to jakoś zasymulować?stream.ordered().forPairs()
czystream().pairMap().forEachOrdered()
?pairMap
jest to operacja pośrednia z niezakłócającą bezstanową funkcją mapowania, kolejność nie jest określona dla niej w taki sam sposób, jak dla prostegomap
. Specyfikacja nieforPairs
jest uporządkowana, ale operacje nieuporządkowane są de facto uporządkowane dla strumieni sekwencyjnych. Byłoby miło, gdybyś sformułował swój pierwotny problem jako oddzielne pytanie dotyczące przepływu stosu, aby zapewnić więcej kontekstu.Biblioteka strumieni Java 8 jest głównie nastawiona na dzielenie strumieni na mniejsze fragmenty w celu przetwarzania równoległego, więc etapy stanowe potoku są dość ograniczone, a wykonywanie takich czynności, jak uzyskiwanie indeksu bieżącego elementu strumienia i uzyskiwanie dostępu do sąsiednich elementów strumienia, nie jest obsługiwane.
Typowym sposobem rozwiązania tych problemów, oczywiście z pewnymi ograniczeniami, jest kierowanie strumienia za pomocą indeksów i poleganie na przetwarzaniu wartości w jakiejś strukturze danych o swobodnym dostępie, takiej jak ArrayList, z której można pobrać elementy. Gdyby wartości były w
arrayList
, można by wygenerować pary zgodnie z żądaniem, wykonując coś takiego:Oczywiście ograniczeniem jest to, że wejście nie może być nieskończonym strumieniem. Ten potok może jednak przebiegać równolegle.
źródło
arrayList
) jest w rzeczywistości zbiorem, dlatego nie oznaczyłem go jako odpowiedź. (Ale gratulacje dla twojej złotej odznaki!)To nie jest eleganckie, to hakerskie rozwiązanie, ale działa dla nieskończonych strumieni
Teraz możesz ograniczyć swój strumień do żądanej długości
PS Mam nadzieję, że jest lepsze rozwiązanie, coś w rodzaju clojure
(partition 2 1 stream)
źródło
parallelStream
dokumentu: „Aby zachować prawidłowe zachowanie, te parametry behawioralne muszą być niezakłócające, aw większości przypadków muszą być bezpaństwowe”Zaimplementowałem opakowanie spliteratora, które pobiera wszystkie
n
elementyT
z oryginalnego spliteratora i produkujeList<T>
:Do stworzenia kolejnego strumienia można użyć następującej metody:
Przykładowe użycie:
źródło
List<E>
elementy. Każda lista zawieran
kolejne elementy z oryginalnego strumienia. Sprawdź sam;)(partition size step)
funkcję i jest to najlepszy sposób na jej uzyskanie.ArrayDeque
dla wydajności, zamiastLinkedList
.Możesz to zrobić za pomocą metody Stream.reduce () (nie widziałem żadnych innych odpowiedzi wykorzystujących tę technikę).
źródło
Możesz to zrobić w Cyclops-React (współtworzę tę bibliotekę), używając przesuwanego operatora.
Lub
Zakładając, że konstruktor Pair może zaakceptować Collection z 2 elementami.
Jeśli chcesz pogrupować o 4 i zwiększyć o 2, to również jest obsługiwane.
Równoważne metody statyczne do tworzenia przesuwanego widoku w java.util.stream.Stream są również dostępne w klasie StreamUtils cyclops-streams .
Uwaga: - dla operacji jednowątkowych bardziej odpowiednie byłoby ReactiveSeq. LazyFutureStream rozszerza ReactiveSeq, ale jest przede wszystkim nastawiony na jednoczesne / równoległe użycie (jest to strumień kontraktów futures).
LazyFutureStream rozszerza ReactiveSeq, który rozszerza Seq z niesamowitego jOOλ (który rozszerza java.util.stream.Stream), więc rozwiązania prezentowane przez Lukasa działałyby również z każdym typem Stream. Dla każdego zainteresowanego podstawowymi różnicami między operatorami okien / przesuwnych są oczywisty względny kompromis między mocą a złożonością oraz przydatność do użycia z nieskończonymi strumieniami (przesuwanie nie zużywa strumienia, ale buforuje podczas jego przepływu).
źródło
Biblioteka proton-pack zapewnia okienkiem functionnality. Biorąc pod uwagę klasę pary i strumień, możesz to zrobić w następujący sposób:
Teraz
pairs
strumień zawiera:źródło
st
dwa razy! Czy ta biblioteka może rozwiązać problem za pomocą jednego strumienia?windowed
funkcjonalność! Zobacz edycję.Znajdowanie kolejnych par
Jeśli chcesz skorzystać z biblioteki innej firmy i nie potrzebujesz równoległości, jOOλ oferuje następujące funkcje okna w stylu SQL
Wydajność
lead()
Funkcja uzyskuje dostęp do kolejnej wartości w porządku przemierzania z okna.Znajdowanie kolejnych potrójnych / poczwórnych / n-krotek
Pytanie w komentarzach dotyczyło bardziej ogólnego rozwiązania, w którym nie należy gromadzić par, ale n-krotek (lub ewentualnie list). Oto więc alternatywne podejście:
Udostępnianie listy list
Bez tego
filter(w -> w.count() == n)
wynik byłbyZastrzeżenie: pracuję dla firmy stojącej za jOOλ
źródło
w.lead().lead()
?tuple(w.value(), w.lead(1), w.lead(2))
byłaby opcją. Zaktualizowałem moją odpowiedź o bardziej ogólne rozwiązanie dlalength = n
.window()
nie jest to leniwa operacja, która zbiera cały strumień wejściowy do jakiejś kolekcji pośredniej, a następnie tworzy z niej nowy strumień?Comparator
służy do zmiany kolejności okien), a następnie optymalizacja jak to będzie możliwe, a to może być realizowane w przyszłości.Streams.zip(..)
jest dostępny w guawie , dla tych, którzy na nim polegają.Przykład:
źródło
Możemy użyć RxJava (bardzo potężna reaktywna biblioteka rozszerzeń )
źródło
Observable.zip(obs, obs.skip(1), pair->{...})
Do tej pory używałem ! Nie wiedziałem,Observable.buffer
że mam wersję z krokiem (i jestem przyzwyczajony dozip
sztuczki z Pythona). +1Operacja jest zasadniczo stanowa, więc nie do końca, jakie strumienie mają rozwiązać - zobacz sekcję „Zachowania bezstanowe” w javadoc :
Jednym z rozwiązań jest wprowadzenie stanu do strumienia przez zewnętrzny licznik, chociaż będzie to działać tylko ze strumieniem sekwencyjnym.
źródło
Stream
:! = "Lambdy".StreamEx
Biblioteka jest również dobre znaleźć i może być odpowiedź w sobie. Mój komentarz na temat „strumienie! = Lambdy” odnosi się do stwierdzenia: „Operacja jest zasadniczo stanowa, więc nie do końca, jakie lambdy mają rozwiązać”. Myślę, że chciałeś użyć słowa „strumienie”.W twoim przypadku napisałbym moją niestandardową funkcję IntFunction, która śledzi ostatni przekazany int i użyję jej do zmapowania oryginalnego IntStream.
źródło
Do obliczenia kolejnych różnic w czasie (wartości x) z szeregów czasowych, używać
stream
„Scollect(...)
sposób:Gdzie DifferenceCollector wygląda mniej więcej tak:
Prawdopodobnie możesz to zmodyfikować, aby dopasować je do swoich potrzeb.
źródło
W końcu znalazłem sposób na oszukanie Stream.reduce, aby móc starannie radzić sobie z parami wartości; istnieje wiele przypadków użycia, które wymagają tej funkcji, która nie pojawia się naturalnie w JDK 8:
Sztuczka, której używam, to właściwy powrót; komunikat.
źródło
reduce
daje wystarczających gwarancji, aby to zadziałało.Eleganckim rozwiązaniem byłoby użycie zamka błyskawicznego . Coś jak:
Jest to dość zwięzłe i eleganckie, jednak używa listy jako danych wejściowych. Nieskończone źródło strumienia nie może być przetwarzane w ten sposób.
Innym (dużo bardziej kłopotliwym) problemem jest to, że zip wraz z całą klasą Streams został ostatnio usunięty z API. Powyższy kod działa tylko z wersjami b95 lub starszymi. Więc w przypadku najnowszego JDK powiedziałbym, że nie ma eleganckiego rozwiązania w stylu FP i teraz możemy mieć tylko nadzieję, że w jakiś sposób zip zostanie ponownie wprowadzony do API.
źródło
zip
został usunięty. Nie pamiętam wszystkiego, co było wStreams
klasie, ale niektóre rzeczy zostały przeniesione do statycznych metod wStream
interfejsie, są teżStreamSupport
iStream.Builder
klasy.zip
? Jakikolwiek pedantyczny powód zostałby wymyślony, nie usprawiedliwia zabijaniazip
.To ciekawy problem. Czy moja próba hybrydy poniżej jest dobra?
Uważam, że nie nadaje się do przetwarzania równoległego, a zatem może zostać zdyskwalifikowany.
źródło
Stream
, a nieList
. Oczywiście możemy również pobrać iterator ze strumienia, więc może to być prawidłowe rozwiązanie. Niemniej jednak jest to oryginalne podejście.Jak zauważyli inni, ze względu na naturę problemu wymagana jest pewna stanowość.
Miałem podobny problem, w którym chciałem, aby w istocie była to funkcja Oracle SQL LEAD. Moja próba wdrożenia tego znajduje się poniżej.
źródło
Możesz to osiągnąć, używając ograniczonej kolejki do przechowywania elementów, które przepływają przez strumień (co opiera się na pomyśle, który szczegółowo opisałem tutaj: Czy można pobrać następny element w Stream? )
Poniższy przykład najpierw definiuje instancję klasy BoundedQueue, która będzie przechowywać elementy przechodzące przez strumień (jeśli nie podoba ci się pomysł rozszerzenia LinkedList, odwołaj się do wspomnianego powyżej linku, aby uzyskać alternatywne i bardziej ogólne podejście). Później wystarczy połączyć dwa kolejne elementy w instancję Pair:
źródło
Zgadzam się z @aepurniet, ale zamiast map musisz użyć mapToObj
źródło
Uruchom
for
pętlę, która biegnie od 0 dolength-1
strumieniaźródło