Czy rozsądnie jest zwracać strumienie wszędzie tam, gdzie normalnie zwracamy kolekcje?

19

Podczas opracowywania mojego interfejsu API, który nie jest powiązany z żadnym starszym kodem, często piszę metody, które są czysto strumieniowe przesyłaniem strumieniowym zakończone przez zbieranie wyników. Jak ten:

ImmutableSet<T> deriveSomethingMeaningfulFromPrivateState() {
    return myPrivateThingies.stream()
        .map(this::ownerOfThing)
        .map(Owner::socialStatus)
        .filter(SocialStatus::isHeAFineMatey)
        .collect(MyCustomCollectors.toImmutableSet());
}

Teraz większość klientów tej klasy zwykle potrzebuje kolekcji (w tym przypadku ImmutableSet) do wyszukiwania elementów i iteracji nad nią, ale niektórzy klienci mogą skorzystać z posiadania strumienia, aby mogli nadać temu więcej operacji Przesyłaj strumieniowo bez potrzeby uzyskiwania nowego strumienia z kolekcji. Tak więc zwrócenie Strumienia daje clienom zestaw opcji, które mieliby, gdyby mieli właśnie Kolekcję (w końcu zawsze mogą collect()sami Strumień:

Stream<T> deriveSomethingMeaningfulFromPrivateState() {
    return myPrivateThingies.stream()
        .map(this::ownerOfthing)
        .map(Owner::socialStatus)
        .filter(SocialStatus::isHeAFineMatey);
        // No collect
}

To podejście kusi mnie do wypróbowania, ponieważ nie widzę żadnych potencjalnych wad. Jednak nigdy nie widziałem takiego podejścia w żadnej bibliotece (prawdopodobnie dlatego, że nie było wielu bibliotek wydanych po pojawieniu się Javy 8), więc boję się go przyjąć. Istniejące klasy bibliotek zwykle zwracają Kolekcje, gdy wywodzą coś ze stanu prywatnego.

Czy może się zdarzyć coś złego, jeśli zdecyduję się zwrócić strumień wszędzie tam, gdzie moja jaźń wcześniejsza niż Java-8 zwróciłaby kolekcję? A może robię tu coś w rodzaju antypatternu z tym wszystkim, co pochodzi od państwa prywatnego?

Jojman
źródło

Odpowiedzi:

14

Jeśli myPrivateThingiesmożna go modyfikować, utworzono ukrytą zależność między stanem prywatnym a wynikami strumienia. Jeśli możliwe jest, że klient pośrednio spowoduje myPrivateThingieszmianę stanu, wtedy uzyska inny wynik podczas połączenia collectniż ten, który pierwotnie zamierzałeś rozdać.

Jeśli myPrivateThingiesjest niezmienny, wynik będzie referencyjnie przezroczysty, ale jest jeszcze jeden problem, na który trzeba uważać: śmieci semantyczne , tj. Utrzymywanie dużej ilości pamięci, która nie jest już potrzebna. Załóżmy, że myPrivateThingiesjest bardzo duży, a wynik zbierania strumienia jest niewielki. Klient może trzymać się strumienia długo po odrzuceniu wszystkich odniesień do obiektu, który go wytworzył, ale streamnadal nie może myPrivateThingieszostać wyrzucony . Chętnie zbieranie wyników pozwoliłoby myPrivateThingiesna uwolnienie.

Stało się tak przed Java 7 podczas połączenia substring. Oracle zdecydowało, że potencjalne oszczędności wydajności wynikające z nie kopiowania podciągu za każdym razem nie są warte zaskakiwania przeciętnego użytkownika nadmiernym zużyciem pamięci. Nie oznacza to, że nie było prawdziwych przypadków użycia dla starego zachowania (np. Parserów), ale często gromadzenie wyników z niecierpliwością jest wystarczająco szybkie, a kiedy to się dzieje, nie masz żadnych zalet i potencjalnego oszustwa.

Z drugiej strony zwracanie strumienia daje klientowi możliwość wyboru struktury danych, której chce użyć do przechowywania wyników, w przeciwieństwie do wybrania dla niego jednej. Może warto zaoferować obie opcje.

Doval
źródło
4

Najważniejszą rzeczą do rozważenia: Streams można powtórzyć tylko raz, podczas gdy masz większą elastyczność w stosunku do Collection: możesz nadal tworzyć więcej Streams lub nawet Iterators, aby wykonać dodatkowe, powtarzalne przetwarzanie wyników.

Więc jeśli nie masz pewności, czy osoby wywołujące metodę będą używać wyników raz i tylko raz, lepiej zwrócić wartość a Collection.


Twój przykładowy kod zawiera jeden oczywisty błąd: dlaczego miałby SocialStatusmieć pojęcie osoby he?

hjk
źródło
3

Moim zdaniem nie. To, co możesz zrobić ze strumieniami, to ścisły nadzór nad tym, co możesz zrobić z kolekcjami, i często można je usprawnić, więc nie ma powodu, aby ich nie używać, z wyjątkiem nieznajomości. „Wyrażenia lambda są bramą do Java 8, ale strumienie są prawdziwym uzależnieniem”. (Venkat Subramaniam, Programowanie funkcjonalne w Javie )

Kilian Foth
źródło