Załóżmy, że mam strumień Rzeczy i chcę je „wzbogacić” w połowie strumienia, mogę peek()
to zrobić, np .:
streamOfThings.peek(this::thingMutator).forEach(this::someConsumer);
Załóżmy, że mutowanie Rzeczy w tym punkcie kodu jest poprawnym zachowaniem - na przykład thingMutator
metoda może ustawić pole „lastProcessor” na bieżący czas.
Jednak peek()
w większości kontekstów oznacza „patrz, ale nie dotykaj”.
Używa peek()
się mutować elementów Stream antywzorzec projektowy lub nierozważne?
Edytować:
Alternatywnym, bardziej konwencjonalnym podejściem byłoby przekonanie konsumenta:
private void thingMutator(Thing thing) {
thing.setLastProcessed(System.currentTimeMillis());
}
do funkcji, która zwraca parametr:
private Thing thingMutator(Thing thing) {
thing.setLastProcessed(currentTimeMillis());
return thing;
}
i użyj map()
zamiast tego:
stream.map(this::thingMutator)...
Ale to wprowadza kod obłędny ( return
) i nie jestem przekonany, że jest bardziej przejrzysty, ponieważ wiesz, że peek()
zwraca ten sam obiekt, ale map()
na pierwszy rzut oka nie jest nawet jasne, że jest to ta sama klasa obiektu.
Co więcej, peek()
możesz mieć mutację lambda, ale wraz z map()
tobą musisz zbudować wrak pociągu. Porównać:
stream.peek(t -> t.setLastProcessed(currentTimeMillis())).forEach(...)
stream.map(t -> {t.setLastProcessed(currentTimeMillis()); return t;}).forEach(...)
Myślę, że peek()
wersja jest wyraźniejsza, a lambda wyraźnie mutuje, więc nie ma „tajemniczego” efektu ubocznego. Podobnie, jeśli używane jest odwołanie do metody, a nazwa metody wyraźnie implikuje mutację, to również jest jasne i oczywiste.
Osobiście nie waham się używać peek()
mutacji - uważam to za bardzo wygodne.
peek
ze strumienia, który dynamicznie generuje jego elementy? Czy to nadal działa, czy zmiany zostały utracone? Modyfikowanie elementów strumienia wydaje mi się niewiarygodne.List<Thing> list; things.stream().peek(list::add).forEach(...);
bardzo przydatny. Ostatnio. Używałem go dodać informacje do publikacji:Map<Thing, Long> timestamps = ...; return things.stream().peek(t -> t.setTimestamp(timestamp.get(t))).collect(toList());
. Wiem, że istnieją inne sposoby na zrobienie tego przykładu, ale tutaj nadmiernie upraszczam. Korzystaniepeek()
tworzy bardziej zwarty i bardziej elegancki kod IMHO. Pomijając czytelność, to pytanie naprawdę dotyczy tego, co poruszyłeś; czy to jest bezpieczne / niezawodne?peek
? Mam podobne pytanie na temat przepełnienia stosu i mam nadzieję, że mógłbyś na nie spojrzeć i wyrazić swoją opinię. Dzięki. stackoverflow.com/questions/47356992/…Odpowiedzi:
Masz rację, „zerknięcie” w angielskim znaczeniu oznacza „patrz, ale nie dotykaj”.
Jednak gdy javadoc stany:
Słowa kluczowe: „wykonywanie ... akcji” i „pochłonięty”. JavaDoc jest bardzo jasne, że powinniśmy oczekiwać
peek
możliwości modyfikacji strumienia.Jednak JavaDoc stwierdza również:
Wskazuje to, że jest przeznaczony bardziej do obserwacji, np. Rejestruje elementy w strumieniu.
Biorę z tego wszystko, że możemy wykonywać działania przy użyciu elementów w strumieniu, ale powinniśmy unikać mutowania elementów w strumieniu. Na przykład idź dalej i wywoływaj metody na obiektach, ale staraj się unikać mutowania na nich operacji.
Przynajmniej dodam krótki komentarz do twojego kodu w następujący sposób:
Opinie na temat przydatności takich komentarzy są różne, ale użyłbym takiego komentarza w tym przypadku.
źródło
thingMutator
Lub bardziej konkretnąresetLastProcessed
itp. O ile nie ma ważnego powodu, komentarze takie jak twoja sugestia zazwyczaj wskazują na zły wybór nazw zmiennych i / lub metod. Jeśli wybierzesz dobre imię, czy to nie wystarczy? A może mówisz, że pomimo dobrego imienia, wszystkopeek()
jest jak martwy punkt, który większość programistów „przeskakuje” (wizualnie pomija)? Ponadto „głównie do debugowania” nie jest tym samym, co „tylko do debugowania” - jakie zastosowania inne niż te „głównie” były zamierzone?Może być łatwo źle zinterpretowany, więc unikałbym używania go w ten sposób. Prawdopodobnie najlepszą opcją jest użycie funkcji lambda do połączenia dwóch potrzebnych działań w wywołanie forEach. Możesz również rozważyć zwrócenie nowego obiektu zamiast mutowania istniejącego - może być nieco mniej wydajny, ale może skutkować bardziej czytelnym kodem i zmniejsza ryzyko przypadkowego użycia zmodyfikowanej listy do innej operacji, która powinna otrzymałem oryginał.
źródło
Nota API mówi nam, że metoda została dodana głównie do akcji takich jak debugowanie / rejestrowanie / odpalanie statystyk itp.
@apiNote Ta metoda istnieje głównie w celu obsługi debugowania, w której chcesz * widzieć elementy przepływające przez określony punkt w potoku: *
źródło