É sensato devolver o Streams para onde normalmente devolveríamos Coleções?

19

Ao desenvolver minha API que não está vinculada a nenhum código legado, muitas vezes me pego escrevendo métodos que são puramente pipeline do Streams encerrados coletando os resultados. Como este:

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

Agora, a maioria dos clientes dessa classe geralmente precisará da Collection (neste caso, um ImmutableSet) para pesquisar elementos e iterar sobre ela, mas alguns clientes podem se beneficiar de ter um Stream para poder canalizar mais algumas operações sobre isso. Transmita sem a necessidade de obter um novo fluxo da Coleção. Portanto, retornar um Stream dá aos clientes um superconjunto de opções que eles teriam se tivessem apenas a Coleção (afinal, eles sempre podem collect()o Stream:

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

Essa abordagem é tentadora para eu experimentar, pois não vejo nenhuma falha em potencial que possa ter. No entanto, nunca vi essa abordagem em nenhuma biblioteca (provavelmente porque não havia muitas bibliotecas lançadas após o aparecimento do Java 8), por isso tenho um pouco de medo de adotá-la. As classes de biblioteca existentes geralmente retornam Coleções quando derivam algo do estado privado.

Existe algo de ruim que poderia acontecer se eu decidisse retornar um Stream sempre que meu eu pré-Java-8 retornasse uma Coleção? Ou provavelmente estou fazendo algo antipadrão aqui com tudo isso derivado do estado privado?

jojman
fonte

Respostas:

14

Se myPrivateThingiesfor mutável, você criou uma dependência oculta entre o seu estado privado e os resultados do fluxo. Se for possível que o cliente induza indiretamente myPrivateThingiesa mudança de estado, ele obterá um resultado diferente ao chamar do collectque aquele que você pretendia dar originalmente.

Se myPrivateThingiesfor imutável, o resultado será referencialmente transparente, mas há mais um problema a ser observado: lixo semântico , ou seja, armazenar grandes quantidades de memória que não são mais necessárias. Suponha que myPrivateThingiesseja muito grande e o resultado da coleta do fluxo seja pequeno. O cliente pode manter o fluxo por muito tempo depois de ter descartado todas as referências ao objeto que o produziu, mas isso streamainda myPrivateThingiesimpede a coleta de lixo. Ansiosamente, coletar os resultados permitiria myPrivateThingiesser liberado.

Na verdade, isso aconteceu antes do Java 7 ao chamar substring. A Oracle decidiu que as economias potenciais de eficiência em não copiar a substring toda vez que não valem a pena surpreender ocasionalmente o usuário comum com consumo excessivo de memória. Isso não quer dizer que não houve casos de uso reais para o comportamento antigo (por exemplo, analisadores), mas muitas vezes a coleta dos resultados é rápida o suficiente e, quando isso acontece, você não tem profissionais nem um possível embuste.

Por outro lado, o retorno de um fluxo oferece ao cliente a capacidade de escolher qual estrutura de dados ele deseja usar para manter os resultados, em vez de você escolher um para ele. Pode valer a pena oferecer as duas opções.

Doval
fonte
4

A coisa mais importante a considerar: Streams só pode ser iterado uma vez, enquanto você tem mais flexibilidade em relação a Collection: você pode continuar a criar mais Streams ou mesmo Iterators para realizar um processamento repetitivo adicional nos resultados.

Portanto, se você não tiver certeza se os chamadores do método usarão os resultados uma e apenas uma vez, é melhor retornar a Collection.


Seu código de exemplo tem um erro óbvio: por que a SocialStatustem o conceito de pessoa he?

hjk
fonte
3

Na minha opinião, não. As coisas que você pode fazer com fluxos são um superconjunto estrito das coisas que você pode fazer com coleções e, com frequência, elas podem se tornar mais eficientes; portanto, não há razão para não usá-las, exceto a falta de familiaridade. "As expressões Lambda são o remédio para o Java 8, mas o Streams é o verdadeiro vício". (Venkat Subramaniam, Programação Funcional em Java )

Kilian Foth
fonte