Разумно ли возвращать потоки везде, где мы обычно возвращаем коллекции?

19

Разрабатывая мой API, который не привязан к какому-либо устаревшему коду, я часто нахожу себя пишущим методы, которые являются чисто конвейерными потоками и заканчиваются сбором результатов. Как этот:

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

Теперь большинству клиентов этого класса обычно требуется Collection (в данном случае ImmutableSet) для поиска элементов и итерации по нему, но некоторым клиентам может быть полезно иметь Stream, чтобы они могли передавать еще несколько операций поверх этого. Поток без необходимости получать новый поток из Коллекции. Поэтому возвращение потока дает клиентам расширенный набор опций, которые они имели бы, если бы у них была только коллекция (в конце концов, они всегда могут collect()сами использовать поток:

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

Такой подход заманчив для меня, потому что я не вижу никаких потенциальных недостатков, которые он мог бы иметь. Однако я никогда не видел такого подхода ни в одной библиотеке (возможно, потому что не было много библиотек, выпущенных после появления Java 8), поэтому я немного боюсь его принять. Существующие библиотечные классы обычно возвращают Коллекции, когда они получают что-то из частного состояния.

Есть ли что-то плохое, что может случиться, если я решу вернуть поток там, где я до Java-8 вернул бы коллекцию? Или, возможно, я делаю что-то антипаттерн со всем, что происходит из частного государства?

jojman
источник

Ответы:

14

Если myPrivateThingiesон изменчив, вы создали скрытую зависимость между вашим личным состоянием и результатами потока. Если для клиента возможно косвенное myPrivateThingiesизменение состояния, то при вызове он получит другой результат, collectчем тот, который вы изначально намеревались выдать.

Если значение myPrivateThingiesявляется неизменным, то результат будет прозрачным по ссылкам, но есть еще одна проблема, на которую следует обратить внимание: семантический мусор , т. Е. Удержание большого количества памяти, которая больше не нужна. Предположим, что myPrivateThingiesон очень большой, а результат сбора потока мал. Клиент может удерживать поток долго после того, как отбросил все ссылки на объект, который его создал, но это streamвсе еще не позволяет myPrivateThingiesсобирать мусор. Стремительный сбор результатов позволил myPrivateThingiesбы быть освобожденным.

Это на самом деле произошло до Java 7 при вызове substring. Oracle решила, что потенциальная экономия за счет отсутствия копирования подстроки каждый раз не стоит удивлять среднего пользователя чрезмерным потреблением памяти. Это не значит, что не было реальных вариантов использования старого поведения (например, парсеров), но часто собирать результаты достаточно быстро, и когда это происходит, у вас нет плюсов и потенциальных минусов.

С другой стороны, возврат потока дает клиенту возможность выбрать, какую структуру данных он хочет использовать для хранения результатов, в отличие от того, какой вы выбрали для него. Возможно, стоит предложить оба варианта.

Doval
источник
4

Самая важная вещь, которую следует учитывать: Streams может быть повторен только один раз, тогда как у вас больше гибкости по сравнению с Collection: вы можете продолжать создавать больше Streams или даже Iterators для выполнения дополнительной, повторяющейся обработки результатов.

Так что, если вы не уверены, что вызывающие методы будут использовать результаты один раз и только один раз, лучше вернуть a Collection.


В вашем примере кода есть одна очевидная ошибка: почему у человека SocialStatusесть концепция he?

ХИК
источник
3

На мой взгляд, нет. То, что вы можете делать с потоками, является строгим набором вещей, которые вы можете делать с коллекциями, и часто их можно сделать более эффективными, поэтому нет никаких причин не использовать их, кроме незнакомых вам. «Лямбда-выражения - это шлюз для Java 8, но Streams - настоящая зависимость». (Венкат Субраманиам, Функциональное программирование на Java )

Килиан Фот
источник