Mam następujący kod:
Stream<String> lines = reader.lines();
Jeśli ciąg pięści jest równy "email"
, chcę usunąć pierwszy ciąg ze strumienia. W przypadku innych ciągów ze strumienia nie potrzebuję tego sprawdzania.
Jak mogę to osiągnąć?
PS
Jasne, że mogę przekształcić go w listę, a następnie użyć starej pętli dla starej szkoły, ale dalej potrzebuję strumienia ponownie.
java
java-stream
java-11
gstackoverflow
źródło
źródło
skip(long n)
że pomija pierwszen
elementy, ale nie wiem, czy można to jakoś uwarunkować ...stream.dropWhile(s -> s.equals("email"));
Odpowiedzi:
Podczas gdy czytnik będzie w nieokreślonym stanie po zbudowaniu z niego strumienia linii, jest w dobrze zdefiniowanym stanie, zanim to zrobisz.
Więc możesz to zrobić
Co jest moim zdaniem najczystszym rozwiązaniem. Zauważ, że nie jest to to samo co Java 9
dropWhile
, które spadłyby więcej niż jedną linię, jeśli się zgadzają.źródło
Jeśli nie możesz mieć listy i musisz to zrobić tylko za pomocą a
Stream
, możesz to zrobić za pomocą zmiennej.Chodzi o to, że można użyć zmiennej tylko wtedy, gdy jest ona „ostateczna” lub „efektywnie ostateczna”, więc nie można użyć literalnej wartości logicznej. Nadal możesz to zrobić za pomocą
AtomicBoolean
:Uwaga: Nie lubię używać „zmiennych zewnętrznych”,
Stream
ponieważ skutki uboczne są złą praktyką w paradygmacie programowania funkcjonalnego. Lepsze opcje są mile widziane.źródło
compareAndSet
jest bardzo eleganckim sposobem ustawiania i zdobywania flagi w tym samym czasie, miło.stream.filter(!first.compareAndSet(true, false) || !s.equals("email"))
choćby dyskusyjne.predicate
musi być bezpaństwowcemAtomicBoolean
tutaj służy do obsługi równoległych strumieni (jakcompareAndSet
sugeruje użycie ), jest to całkowicie błędne, ponieważ zadziała tylko wtedy, gdy strumień jest sekwencyjny. Jeśli jednak użyjesz tej technikistream.sequential().filter(...)
, zgadzam się, że zadziała.Aby uniknąć sprawdzania warunku w każdej linii pliku, po prostu czytam i sprawdzam pierwszy wiersz osobno, a następnie uruchamiam potok na pozostałych liniach bez sprawdzania warunku:
Prostsze w Javie 9+:
źródło
Stream.ofNullable(first).filter(not("email"::equals))
Odpowiedź @Arnouds jest prawidłowa. Możesz utworzyć jeden strumień dla pierwszej linii, a następnie porównać jak poniżej,
źródło
limit(0)
pewno powinien byćlimit(1)
….limit(0).filter(line -> !line.startsWith("email"))
nie ma sensu. Predykat nigdy nie zostanie oceniony. W przypadku źródła strumienia zdolnego do odtwarzania strumieni kombinacjalimit(1)
pierwszego iskip(1)
drugiego będzie poprawna. W przypadku źródła strumienia, takiego jak czytnik, anilimit(1)
nielimit(0)
będzie działać. Właśnie zmieniłeś, która linia zostanie bezwarunkowo połknięta. Nawet jeśli znajdziesz kombinację, która przyniesie pożądaną czynność, będzie to wynikać z nieokreślonego zachowania zależnego od implementacji.Aby filtrować elementy na podstawie ich indeksu, możesz użyć
AtomicInteger
do przechowywania i zwiększania indeksu podczas przetwarzaniaStream
:Takie podejście pozwala filtrować elementy według dowolnego indeksu, nie tylko pierwszego.
źródło
Trochę bardziej skomplikowany, czerpiąc inspirację z tego fragmentu .
Możesz utworzyć plik,
Stream<Integer>
który będzie reprezentował indeksy i „spakować” go,Stream<String>
aby utworzyć plikStream<Pair<String, Integer>>
Następnie filtruj za pomocą indeksu i zamapuj go z powrotem na
Stream<String>
źródło
Oto przykład z
Collectors.reducing
. Ale ostatecznie i tak tworzy listę.źródło
Spróbuj tego:
źródło