Pytanie ogólne: Jaki jest właściwy sposób odwrócenia strumienia? Zakładając, że nie wiemy, z jakiego typu elementów składa się strumień, jaki jest ogólny sposób odwrócenia dowolnego strumienia?
Konkretne pytanie:
IntStream
zapewnia metodę zakresu do generowania liczb całkowitych w określonym zakresie IntStream.range(-range, 0)
, teraz, gdy chcę to odwrócić, przełączanie zakresu od 0 do ujemnego nie zadziała, również nie mogę użyćInteger::compare
List<Integer> list = Arrays.asList(1,2,3,4);
list.stream().sorted(Integer::compare).forEach(System.out::println);
z IntStream
otrzymam ten błąd kompilatora
Błąd: (191, 0) ajc: Metoda
sorted()
w typieIntStream
nie ma zastosowania do argumentów (Integer::compare
)
czego tu brakuje?
IntStream
nie ma.sorted(Comparator)
metody; musisz przejść przezStream<Integer>
pierwszy i tam zawrócić, zanimIntStream
IntStream.range(0, n)
w odwrotnej kolejności, zrób coś takiegomap(i -> n - i - 1)
. Nie musisz robić boksu i sortować.1, 3, 2
, jaki jest twój oczekiwany wynik? Czy chcesz, aby strumień był odwrócony,2, 3, 1
czy posortowany3, 2, 1
?Odpowiedzi:
Jeśli chodzi o konkretną kwestię generowania rewersu
IntStream
, spróbuj czegoś takiego:Pozwala to uniknąć boksowania i sortowania.
Jeśli chodzi o ogólne pytanie, jak odwrócić strumień dowolnego typu, nie wiem, czy istnieje „właściwy” sposób. Mogę wymyślić kilka sposobów. Oba kończą się przechowywaniem elementów strumienia. Nie znam sposobu na odwrócenie strumienia bez przechowywania elementów.
Ten pierwszy sposób zapisuje elementy w tablicy i odczytuje je do strumienia w odwrotnej kolejności. Zwróć uwagę, że ponieważ nie znamy typu środowiska wykonawczego elementów strumienia, nie możemy poprawnie wpisać tablicy, co wymaga niezaznaczonego rzutowania.
Inna technika wykorzystuje kolekcjonerów do gromadzenia elementów w odwróconej liście. To powoduje wiele wstawek z przodu
ArrayList
obiektów, więc jest dużo kopiowania.Prawdopodobnie jest możliwe napisanie znacznie bardziej wydajnego kolektora wstecznego przy użyciu jakiejś niestandardowej struktury danych.
UPDATE 2016-01-29
Ponieważ ostatnio zwrócono uwagę na to pytanie, myślę, że powinienem zaktualizować swoją odpowiedź, aby rozwiązać problem z wstawianiem z przodu
ArrayList
. Będzie to okropnie nieefektywne przy dużej liczbie elementów, wymagającym kopiowania O (N ^ 2).Lepiej jest użyć
ArrayDeque
zamiast tego, który skutecznie wspiera wkładanie z przodu. Mała zmarszczka polega na tym, że nie możemy użyć formy trójargumentowejStream.collect()
; wymaga, aby zawartość drugiego argumentu została scalona z pierwszym argumentem i nie ma żadnej operacji zbiorczej typu „dodaj wszystko z przodu”Deque
. Zamiast tegoaddAll()
dodajemy zawartość pierwszego argumentu do końca drugiego, a następnie zwracamy drugi. Wymaga to użyciaCollector.of()
metody fabrycznej.Kompletny kod jest następujący:
Rezultatem jest a
Deque
zamiast aList
, ale nie powinno to stanowić większego problemu, ponieważ można go łatwo iterować lub przesyłać strumieniowo w odwróconej kolejności.źródło
IntStream.iterate(to-1, i->i-1).limit(to-from)
.limit(endExcl-(long)startIncl)
zamiast tego, ale w przypadku tak dużych strumieni jest to i tak bardzo odradzane, ponieważ jest znacznie mniej wydajne niżrange
rozwiązanie bazowe . W czasie, gdy pisałem komentarz, nie byłem świadomy różnicy w wydajności.Eleganckie rozwiązanie
źródło
Comparable
...Wiele z przedstawionych tutaj rozwiązań sortuje lub odwraca
IntStream
, ale to niepotrzebnie wymaga pośredniego przechowywania. Rozwiązanie Stuarta Marksa jest do zrobienia:Prawidłowo radzi sobie również z przepełnieniem, przechodząc ten test:
źródło
Estreams
nazwy (zamierzam usunąć ją z postu). To jedna z klas użytkowych wewnętrznych naszej firmy, których używamy do uzupełnieniajava.util.stream.Stream
„sstatic
metod.StreamEx
określając krok:IntStreamEx.rangeClosed(from-1, to, -1)
Pytanie ogólne:
Stream nie przechowuje żadnych elementów.
Dlatego iterowanie elementów w odwrotnej kolejności nie jest możliwe bez przechowywania elementów w jakiejś kolekcji pośredniej.
Aktualizacja: Zmieniono LinkedList na ArrayDeque (lepiej), patrz tutaj, aby uzyskać szczegółowe informacje
Wydruki:
Nawiasem mówiąc, użycie
sort
metody nie jest poprawne, ponieważ sortuje, NIE odwraca (zakładając, że strumień może mieć nieuporządkowane elementy)Konkretne pytanie:
Znalazłem to proste, łatwiejsze i intuicyjne (skopiowany komentarz @Holger )
źródło
sorted
idistinct
faktycznie przechowują wynik pośredni. Zobacz dokumentację pakietu API, aby uzyskać więcej informacji na ten temat.No storage
na tej samej stronie. Nawet jeśli przechowuje, nie możemy uzyskać dostępu do tego magazynu (więcNo storage
myślę, że jest w porządku)bez biblioteki zewnętrznej ...
źródło
Jeśli realizowane
Comparable<T>
(np.Integer
,String
,Date
), Można to zrobić za pomocąComparator.reverseOrder()
.źródło
Stream.of(1,3,2)
wynik byłbyStream.of(3,2,1)
NIEStream.of(2,3,1)
Możesz zdefiniować własny kolektor, który zbiera elementy w odwrotnej kolejności:
I używaj go jak:
Używam ArrayList w celu wydajnego wstawiania zbierania elementów (na końcu listy) i Guava Lists.reverse, aby efektywnie dawać odwrócony widok listy bez wykonywania jej kolejnej kopii.
Oto kilka przypadków testowych dla niestandardowego modułu zbierającego:
źródło
cyclops- interact StreamUtils ma metodę odwróconego strumienia ( javadoc ).
Działa zbierając do ArrayList, a następnie wykorzystując klasę ListIterator, która może iterować w dowolnym kierunku, aby wykonać iterację wstecz po liście.
Jeśli masz już listę, będzie bardziej wydajna
źródło
Sugerowałbym użycie jOOλ , to świetna biblioteka, która dodaje wiele przydatnych funkcji do strumieni Java 8 i lambd.
Następnie możesz wykonać następujące czynności:
Proste. Jest to dość lekka biblioteka, którą warto dodać do każdego projektu Java 8.
źródło
Oto rozwiązanie, które wymyśliłem:
następnie używając tych komparatorów:
źródło
Collections.reverseOrder()
istnieje od wersji Java 1.2 i współpracuje zInteger
…A co z tą metodą użytkową?
Wydaje się działać we wszystkich przypadkach bez powielania.
źródło
źródło
Najprostszy sposób (proste zbieranie - obsługuje równoległe strumienie):
Zaawansowany sposób (obsługuje strumienie równoległe w sposób ciągły):
Zauważ, że możesz szybko rozszerzyć na inne typy strumieni (IntStream, ...).
Testowanie:
Wyniki:
Dodatkowe uwagi: Nie
simplest way
jest to tak przydatne, gdy jest używane z innymi operacjami na strumieniu (łączenie kolekcjonowania przerywa równoległość).advance way
Nie ma tego problemu, a także zachowuje pierwotne właściwości strumienia, na przykładSORTED
, i tak, jest to droga do użytku z innych operacji strumienia po odwrocie.źródło
Można by napisać kolektor, który zbiera elementy w odwrotnej kolejności:
I użyj tego w ten sposób:
Oryginalna odpowiedź (zawiera błąd - nie działa poprawnie dla równoległych strumieni):
Metoda odwrócenia strumienia ogólnego przeznaczenia może wyglądać następująco:
źródło
Nie tylko Java8, ale jeśli użyjesz metody Lists.reverse () guawy w połączeniu, możesz łatwo osiągnąć to:
źródło
W odniesieniu do konkretnej kwestii generowania rewersu
IntStream
:zaczynając od Java 9 możesz użyć trzyargumentowej wersji
IntStream.iterate(...)
:gdzie:
IntStream.iterate(int seed, IntPredicate hasNext, IntUnaryOperator next);
seed
- element początkowy;hasNext
- predykat do zastosowania do elementów w celu określenia, kiedy strumień musi się zakończyć;next
- funkcja, która ma być zastosowana do poprzedniego elementu w celu utworzenia nowego elementu.źródło
Dla odniesienia przyglądałem się temu samemu problemowi, chciałem połączyć wartość ciągu elementów strumienia w odwrotnej kolejności.
itemList = {last, middle, first} => first, middle, last
Zacząłem używać kolekcji z pośrednią
collectingAndThen
z comonad lubArrayDeque
kolektora Stuart towarowe , chociaż nie byłem zadowolony z kolekcji pośredniej i strumieniowe ponowniePowtórzyłem więc odpowiedź Stuarta Marksa
Collector.of
, która dotyczyła fabryki, która ma interesującą lambdę finiszera .Ponieważ w tym przypadku strumień nie jest równoległy, sumator nie jest tak bardzo istotny, i tak używam
insert
ze względu na spójność kodu, ale nie ma to znaczenia, ponieważ zależy to od tego, który kompilator ciągów jest zbudowany jako pierwszy.Spojrzałem na StringJoiner, jednak nie ma on
insert
metody.źródło
Odpowiedź na konkretne pytanie dotyczące cofania za pomocą IntStream, poniżej działała dla mnie:
źródło
ArrayDeque
są szybsze na stosie niż Stack lub LinkedList. „push ()” wstawia elementy z przodu Dequeźródło
Odwracanie ciągu lub dowolnej tablicy
podział można modyfikować na podstawie separatora lub spacji
źródło
najprostszym rozwiązaniem jest użycie
List::listIterator
iStream::generate
źródło
Stream.generate()
generuje się w nieskończonym strumieniu, więc wezwanie dolimit()
jest tutaj bardzo ważne.Tak to robię.
Nie podoba mi się pomysł tworzenia nowej kolekcji i jej odwracania.
Pomysł na mapę IntStream # jest całkiem zgrabny, ale wolę metodę iteracyjną IntStream #, ponieważ myślę, że idea odliczania do zera jest lepiej wyrażona za pomocą metody iteracyjnej i łatwiejsza do zrozumienia pod względem chodzenia po tablicy od tyłu do przodu.
Oto kilka testów, które potwierdzają, że to działa:
źródło
W tym wszystkim nie widzę odpowiedzi, do której bym poszedł pierwszy.
Nie jest to do końca bezpośrednia odpowiedź na pytanie, ale jest to potencjalne rozwiązanie problemu.
Po prostu zbuduj listę od tyłu. Jeśli możesz, użyj LinkedList zamiast ArrayList, a podczas dodawania elementów użyj opcji „Wypchnij” zamiast dodawać. Lista zostanie utworzona w odwrotnej kolejności, a następnie będzie poprawnie przesyłana strumieniowo bez żadnych manipulacji.
Nie będzie to pasowało do przypadków, w których masz do czynienia z prymitywnymi tablicami lub listami, które są już używane na różne sposoby, ale działają dobrze w zaskakującej liczbie przypadków.
źródło
Ta metoda działa z dowolnym strumieniem i jest zgodna z Javą 8:
źródło
Najbardziej ogólną i najłatwiejszą metodą odwrócenia listy będzie:
źródło
Comparator
. W rezultacie nikt nie może zagwarantować, że ta „sztuczka” zadziała w każdej przyszłej wersji Javy z dowolnym algorytmem sortowania. Ta sama sztuczka nie działa na przykład w przypadku strumienia równoległego, ponieważ algorytm sortowania równoległego wykorzystujeComparator
w inny sposób. W przypadku sortowania sekwencyjnego działa to wyłącznie przypadkowo. Nie polecałbym nikomu korzystania z tego rozwiązania.System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
public static <T> void reverseHelper(List<T> li){ li.parallelStream() .sorted((x,y)->-1) .collect(Collectors.toList()) .forEach(System.out::println); }
reverseHelper(IntStream.range(0, 8193).boxed().collect(Collectors.toList()))
(wynik może jednak zależeć od liczby rdzeni).Java 8, jak to zrobić:
źródło