Como posso verificar se a Stream
está vazio e lançar uma exceção se não estiver, como uma operação não terminal?
Basicamente, estou procurando algo equivalente ao código abaixo, mas sem materializar o fluxo intermediário. Em particular, a verificação não deve ocorrer antes que o fluxo seja realmente consumido por uma operação de terminal.
public Stream<Thing> getFilteredThings() {
Stream<Thing> stream = getThings().stream()
.filter(Thing::isFoo)
.filter(Thing::isBar);
return nonEmptyStream(stream, () -> {
throw new RuntimeException("No foo bar things available")
});
}
private static <T> Stream<T> nonEmptyStream(Stream<T> stream, Supplier<T> defaultValue) {
List<T> list = stream.collect(Collectors.toList());
if (list.isEmpty()) list.add(defaultValue.get());
return list.stream();
}
java
java-8
java-stream
Cefalópode
fonte
fonte
Respostas:
Se você pode viver com capacidades paralelas limitadas, a seguinte solução funcionará:
Aqui está um exemplo de código usando-o:
O problema com a execução paralela (eficiente) é que o suporte à divisão do
Spliterator
requer uma maneira segura de thread para perceber se algum dos fragmentos viu algum valor de maneira segura. Então, o último dos fragmentos em execuçãotryAdvance
precisa perceber que é o último (e também não pode avançar) a lançar a exceção apropriada. Portanto, não adicionei suporte para divisão aqui.fonte
As outras respostas e comentários estão corretos no sentido de que para examinar o conteúdo de um fluxo, é necessário adicionar uma operação de terminal, "consumindo" assim o fluxo. No entanto, pode-se fazer isso e transformar o resultado de volta em um fluxo, sem armazenar todo o conteúdo do fluxo. Aqui estão alguns exemplos:
Basicamente, transforme o stream em um
Iterator
para chamáhasNext()
-lo e, se verdadeiro, transforme asIterator
costas em umStream
. Isso é ineficiente porque todas as operações subsequentes no fluxo passarão pelos métodoshasNext()
e do Agente Iterativonext()
, o que também implica que o fluxo é efetivamente processado sequencialmente (mesmo se posteriormente for tornado paralelo). No entanto, isso permite que você teste o fluxo sem armazenar em buffer todos os seus elementos.Provavelmente existe uma maneira de fazer isso usando um em
Spliterator
vez de umIterator
. Isso permite que o fluxo retornado tenha as mesmas características do fluxo de entrada, incluindo a execução em paralelo.fonte
estimatedSize
echaracteristics
pode até melhorar o desempenho de thread único. Acontece que eu escrevi aSpliterator
solução enquanto você estava postando aIterator
solução ...tryAdvance
antes doStream
faz isso transforma a natureza preguiçosa doStream
em um fluxo "parcialmente preguiçoso". Isso também implica que a busca pelo primeiro elemento não é mais uma operação paralela, já que você precisa dividir primeiro e fazertryAdvance
nas partes divididas simultaneamente para fazer uma operação paralela real, pelo que eu entendi. Se a única operação do terminal forfindAny
ou semelhante, isso destruiria toda aparallel()
solicitação.tryAdvance
antes do fluxo e deve envolver cada parte dividida em um proxy e reunir as informações "hasAny" de todas as operações simultâneas por conta própria e garantir que a última operação simultânea emita a exceção desejada se o o riacho estava vazio. Muitas coisas ...Isso pode ser suficiente em muitos casos
fonte
Você deve realizar uma operação de terminal no Stream para que qualquer um dos filtros seja aplicado. Portanto, você não pode saber se ele estará vazio até que você o consuma.
O melhor que você pode fazer é encerrar o Stream com uma
findAny()
operação de terminal, que irá parar quando encontrar qualquer elemento, mas se não houver nenhum, terá que iterar em toda a lista de entrada para descobrir isso.Isso só ajudaria se a lista de entrada tivesse muitos elementos e um dos primeiros passasse pelos filtros, já que apenas um pequeno subconjunto da lista teria que ser consumido antes de você saber que o Fluxo não está vazio.
Claro, você ainda terá que criar um novo fluxo para produzir a lista de saída.
fonte
anyMatch(alwaysTrue())
, eu acho que é o mais próximo dehasAny
.anyMatch(alwaysTrue())
corresponde perfeitamente à semântica pretendida do seuhasAny
, dando-lhe um emboolean
vez deOptional<T>
--- mas estamos dividindo os cabelos aqui :)alwaysTrue
é um predicado Goiaba.anyMatch(e -> true)
então.Eu acho que deve ser o suficiente para mapear um booleano
No código é:
fonte
Stream.anyMatch()
Seguindo a ideia de Stuart, isso poderia ser feito da
Spliterator
seguinte forma:Acho que isso funciona com fluxos paralelos, pois a
stream.spliterator()
operação encerrará o fluxo e, em seguida, o reconstruirá conforme necessárioEm meu caso de uso, eu precisava de um valor padrão em
Stream
vez de um valor padrão. isso é muito fácil de mudar se não for o que você precisafonte
Spliterator
Gostaria de saber como os dois se comparam.