A resposta é, como sempre, "depende". Depende do tamanho da coleção retornada. Depende se o resultado muda ao longo do tempo e qual a importância da consistência do resultado retornado. E isso depende muito de como o usuário provavelmente usará a resposta.
Primeiro, observe que você sempre pode obter uma coleção de um fluxo e vice-versa:
// If API returns Collection, convert with stream()
getFoo().stream()...
// If API returns Stream, use collect()
Collection<T> c = getFooStream().collect(toList());
Portanto, a questão é: o que é mais útil para os chamadores?
Se o resultado for infinito, existe apenas uma opção: Stream.
Se o resultado for muito grande, você provavelmente prefere o Stream, pois pode não haver nenhum valor em materializá-lo de uma só vez, e isso pode criar uma pressão significativa no heap.
Se tudo o que o chamador fizer é iterar através dele (pesquisar, filtrar, agregar), você deve preferir o Stream, já que o Stream já os possui e não há necessidade de materializar uma coleção (especialmente se o usuário não puder processar o resultado completo.) Este é um caso muito comum.
Mesmo que você saiba que o usuário irá iterá-lo várias vezes ou mantê-lo por perto, você ainda pode retornar um Stream, pelo simples fato de que qualquer coleção que você escolher colocar (por exemplo, ArrayList) pode não ser a eles desejam e, em seguida, o chamador deve copiá-lo de qualquer maneira. se você retornar um fluxo, ele pode fazer collect(toCollection(factory))
e obtê-lo exatamente da forma que deseja.
Os casos acima "preferem Stream" derivam principalmente do fato de o Stream ser mais flexível; você pode vincular tarde a como usá-lo sem incorrer nos custos e restrições de materializá-lo em uma coleção.
O único caso em que você deve devolver uma coleção é quando existem requisitos de consistência fortes e é necessário produzir uma captura instantânea consistente de um destino em movimento. Então, você desejará colocar os elementos em uma coleção que não será alterada.
Então, eu diria que na maioria das vezes, o Stream é a resposta certa - é mais flexível, não impõe custos de materialização geralmente desnecessários e pode ser facilmente transformado na coleção de sua escolha, se necessário. Mas, às vezes, talvez você precise devolver uma coleção (por exemplo, devido a fortes requisitos de consistência), ou talvez queira devolver a coleção porque sabe como o usuário a usará e sabe que isso é a coisa mais conveniente para eles.
players.stream()
é exatamente esse método que retorna um fluxo para o chamador. A verdadeira questão é: você realmente deseja restringir o chamador a uma travessia única e também negar a ele o acesso à sua coleção pelaCollection
API? Talvez o interlocutor apenas queira fazeraddAll
isso para outra coleção?