Czy istnieje zwięzły sposób na iterację w strumieniu, mając jednocześnie dostęp do indeksu w strumieniu?
String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
List<String> nameList;
Stream<Integer> indices = intRange(1, names.length).boxed();
nameList = zip(indices, stream(names), SimpleEntry::new)
.filter(e -> e.getValue().length() <= e.getKey())
.map(Entry::getValue)
.collect(toList());
co wydaje się raczej rozczarowujące w porównaniu do podanego tam przykładu LINQ
string[] names = { "Sam", "Pamela", "Dave", "Pascal", "Erik" };
var nameList = names.Where((c, index) => c.Length <= index + 1).ToList();
Czy istnieje bardziej zwięzły sposób?
Ponadto wydaje się, że zamek błyskawiczny został przesunięty lub usunięty ...
java
java-8
java-stream
Graeme Moss
źródło
źródło
intRange()
? Do tej pory nie spotkałem się z tą metodą w Javie 8.IntStream.rangeClosed(x, y)
.List<String> allCities = map.values().stream().flatMap(list -> list.stream()).collect(Collectors.toList());
zip
został usunięty wraz z eksperymentalnymi strumieniami o dwóch wartościach, zwanymi inaczejBiStream
lubMapStream
. Głównym problemem jest to, że aby to zrobić skutecznie, Java naprawdę potrzebuje typu pary (lub krotki) o typie strukturalnym. Brakuje jednego, łatwo jest stworzyć ogólną klasę Pair lub Tuple - robiono to wiele razy - ale wszystkie usuwają się do tego samego typu.Odpowiedzi:
Najczystszym sposobem jest rozpoczęcie od strumienia indeksów:
Wynikowa lista zawiera tylko „Erik”.
Jedną z alternatyw, która wygląda bardziej znajomo, gdy jesteś przyzwyczajony do pętli, byłoby utrzymanie licznika ad hoc przy użyciu obiektu zmiennego, na przykład
AtomicInteger
:Zauważ, że użycie tej drugiej metody w równoległym strumieniu może się zepsuć, ponieważ elementy nie byłyby koniecznie przetwarzane „po kolei” .
źródło
public static <T> Stream<Tuple2<Integer, T>> zipWithIndex(Stream<T> stream) { final AtomicInteger index = new AtomicInteger(); final Function<T, Tuple2<Integer, T>> zipper = e -> Tuples.of(index.getAndIncrement(), e); if (stream.isParallel()) { return stream.sequential().map(zipper).parallel(); } else { return stream.map(zipper); } }
parallel
lubsequential
jest honorowany, gdy rozpoczyna się operacja terminalu.Interfejs API strumieni Java 8 nie ma funkcji pobierania indeksu elementu strumienia, a także możliwości łączenia strumieni razem. Jest to niefortunne, ponieważ sprawia, że niektóre aplikacje (takie jak wyzwania LINQ) są trudniejsze niż w innym przypadku.
Jednak często występują obejścia. Zwykle można tego dokonać „sterując” strumieniem o zakresie liczb całkowitych i wykorzystując fakt, że oryginalne elementy często znajdują się w tablicy lub w kolekcji dostępnej za pomocą indeksu. Na przykład problem Challenge 2 można rozwiązać w następujący sposób:
Jak wspomniałem powyżej, wykorzystuje to fakt, że źródło danych (tablica nazw) można bezpośrednio indeksować. Gdyby tak nie było, ta technika nie działałaby.
Przyznaję, że to nie odpowiada celowi Wyzwania 2. Niemniej jednak rozwiązuje problem dość skutecznie.
EDYTOWAĆ
Mój poprzedni przykład kodu używał
flatMap
do łączenia operacji filtrowania i mapowania, ale było to uciążliwe i nie dawało żadnych korzyści. Zaktualizowałem przykład według komentarza Holgera.źródło
IntStream.range(0, names.length).filter(i->names[i].length()<=i).mapToObj(i->names[i])
? Działa bez boksu…flatMap
?flatMap
ponieważ niejako łączy operację filtrowania i mapowania w pojedynczą operację, ale tak naprawdę nie ma żadnej przewagi. Zmienię przykład.Stream.of( names ).filter( n -> n.length() <= 1).collect( Collectors.toList() );
Mniej rozpakowywania i mniej alokacji pamięci; ponieważ nie tworzymy już strumienia zasięgu.Od guawy 21 możesz używać
Przykład (z oficjalnego dokumentu ):
źródło
W swoim projekcie wykorzystałem następujące rozwiązanie. Myślę, że jest to lepsze niż używanie zmiennych obiektów lub zakresów liczb całkowitych.
źródło
StreamSupport.stream()
oraz niestandardowy iterator.Oprócz protonpack, jOOλ za seq zapewnia tę funkcję (i bibliotek rozszerzeń, które opierają się na nim jak cyklopa reagują , jestem autorem tej biblioteki).
Seq obsługuje również tylko Seq.of (nazwy) i zbuduje strumień JDK pod przykryciem.
Podobnie wyglądałby odpowiednik prostej reakcji
Wersja z prostą reakcją jest bardziej dostosowana do przetwarzania asynchronicznego / równoległego.
źródło
Dla kompletności oto rozwiązanie obejmujące moją bibliotekę StreamEx :
Tutaj tworzymy
EntryStream<Integer, String>
rozszerzenie, które rozszerzaStream<Entry<Integer, String>>
i dodaje określone operacje, takie jakfilterKeyValue
lubvalues
. UżywanytoList()
jest również skrót.źródło
.forEach(entry -> {})
?.forKeyValue((key, value) -> {})
.Znalazłem tutaj rozwiązania, gdy strumień jest tworzony z listy lub tablicy (i znasz rozmiar). Ale co jeśli Stream ma nieznany rozmiar? W takim przypadku wypróbuj ten wariant:
Stosowanie:
źródło
Z listą możesz spróbować
Wynik:
źródło
Nie ma sposobu na iterację przez pewien czas
Stream
, mając dostęp do indeksu, ponieważ aStream
jest inny niż jakikolwiekCollection
. AStream
jest jedynie potokiem do przenoszenia danych z jednego miejsca do drugiego, jak stwierdzono w dokumentacji :Nie przechowywać. Strumień nie jest strukturą danych, która przechowuje elementy; zamiast tego przenoszą wartości ze źródła (którym może być struktura danych, generator, kanał IO itp.) poprzez potok operacji obliczeniowych.
Oczywiście, jak się wydaje na podpowiedź w swoim pytaniu, zawsze możesz przekonwertować swoje
Stream<V>
na aCollection<V>
, takie jak aList<V>
, w którym będziesz miał dostęp do indeksów.źródło
Dzięki https://github.com/poetix/protonpack możesz zrobić to zip:
źródło
Jeśli nie masz nic przeciwko korzystaniu z biblioteki innej firmy, kolekcja Eclipse ma
zipWithIndex
i jestforEachWithIndex
dostępna do użytku na wielu typach. Oto zestaw rozwiązań tego wyzwania dla zarówno typów JDK, jak i typów Eclipse CollectionszipWithIndex
.Oto rozwiązanie wykorzystujące
forEachWithIndex
zamiast tego.Jeśli zmienisz lambdy na anonimowe klasy wewnętrzne powyżej, wówczas wszystkie przykłady kodu będą działały również w Javie 5-7.
Uwaga: jestem osobą odpowiedzialną za kolekcje Eclipse
źródło
Jeśli zdarzy ci się korzystać z Vavr (wcześniej znanego jako JavaScript), możesz skorzystać z dedykowanej metody:
Jeśli wydrukujemy treść, zobaczymy coś interesującego:
Jest tak, ponieważ
Streams
są leniwi i nie mamy pojęcia o kolejnych elementach w strumieniu.źródło
Jeśli próbujesz uzyskać indeks oparty na predykacie, spróbuj tego:
Jeśli zależy Ci tylko na pierwszym indeksie:
Lub jeśli chcesz znaleźć wiele indeksów:
Dodaj,
.orElse(-1);
jeśli chcesz zwrócić wartość, jeśli jej nie znajdzie.źródło
Oto kod AbacusUtil
Ujawnienie : Jestem programistą AbacusUtil.
źródło
Możesz użyć,
IntStream.iterate()
aby uzyskać indeks:Działa to tylko dla Java 9 w górę w Javie 8, możesz użyć tego:
źródło
Możesz utworzyć statyczną klasę wewnętrzną, aby hermetyzować indeksator, tak jak musiałem to zrobić w przykładzie poniżej:
źródło
To pytanie ( Stream Way, aby uzyskać indeks pierwszego elementu pasującego do wartości logicznej ) zaznaczyło bieżące pytanie jako duplikat, więc nie mogę tam odpowiedzieć; Odpowiadam na to tutaj.
Oto ogólne rozwiązanie umożliwiające uzyskanie pasującego indeksu, który nie wymaga biblioteki zewnętrznej.
Jeśli masz listę.
I nazwij to tak:
A jeśli używasz kolekcji, wypróbuj tę.
źródło
Jednym z możliwych sposobów jest indeksowanie każdego elementu w przepływie:
Korzystanie z anonimowej klasy w strumieniu nie jest dobrze używane, ale jest bardzo przydatne.
źródło
nie potrzebujesz
map
koniecznienajbliższej lambda do przykładu LINQ:
źródło
WYDAJNOŚĆ: Sam, Pamela, Dave, Pascal, Erik
Aby zebrać na liście:
źródło
List
jeden element Erik .Jak powiedział jean-baptiste-yunès, jeśli twój strumień jest oparty na liście java, wówczas użycie AtomicInteger i jego metoda incrementAndGet jest bardzo dobrym rozwiązaniem problemu, a zwrócona liczba całkowita odpowiada indeksowi na oryginalnej liście, o ile nie używaj strumienia równoległego.
źródło
Jeśli potrzebujesz indeksu w forEach, zapewnia to sposób.
Następnie użyj go w następujący sposób.
źródło