A nova estrutura de fluxo do Java 8 e os amigos criam um código java muito conciso, mas me deparei com uma situação aparentemente simples que é difícil de fazer de forma concisa.
Considere a List<Thing> things
e método Optional<Other> resolve(Thing thing)
. Quero mapear os Thing
to Optional<Other>
s e obter o primeiro Other
. A solução óbvia seria usar things.stream().flatMap(this::resolve).findFirst()
, mas flatMap
requer que você retorne um fluxo e Optional
não tenha um stream()
método (ou é um Collection
ou fornece um método para convertê-lo ou visualizá-lo como a Collection
).
O melhor que posso apresentar é o seguinte:
things.stream()
.map(this::resolve)
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();
Mas isso parece muito longo para o que parece ser um caso muito comum. Alguem tem uma ideia melhor?
java
lambda
java-8
java-stream
Yona Appletree
fonte
fonte
.flatMap(Optional::toStream)
, à sua versão e você realmente vê o que está acontecendo.Optional.stream
existe no JDK 9 agora ....Respostas:
Java 9
Optional.stream
foi adicionado ao JDK 9. Isso permite que você faça o seguinte, sem a necessidade de qualquer método auxiliar:Java 8
Sim, esse foi um pequeno furo na API, pois é um pouco inconveniente transformar um
Optional<T>
em um comprimento zero ou umStream<T>
. Você pode fazer isso:flatMap
Porém, ter o operador ternário dentro dele é um pouco complicado, portanto, talvez seja melhor escrever uma pequena função auxiliar para fazer isso:Aqui, indiquei a chamada em
resolve()
vez de ter umamap()
operação separada , mas isso é uma questão de gosto.fonte
static <T> Stream<T> streamopt(Optional<T> opt) { return opt.map(Stream::of).orElse(Stream.empty()); }
Optional
sobrecarga paraStream#flatMap
... de que maneira você poderia apenas escreverstream().flatMap(this::resolve)
Optional.stream()
.Estou adicionando esta segunda resposta com base em uma edição proposta pelo usuário srborlongan à minha outra resposta . Eu acho que a técnica proposta foi interessante, mas não foi realmente adequada como uma edição da minha resposta. Outros concordaram e a edição proposta foi rejeitada. (Eu não era um dos eleitores.) A técnica tem mérito, no entanto. Teria sido melhor se srborlongan tivesse postado sua própria resposta. Isso ainda não aconteceu, e eu não queria que a técnica fosse perdida nas brumas do histórico de edição rejeitado do StackOverflow, então decidi apresentá-la como uma resposta separada.
Basicamente, a técnica é usar alguns dos
Optional
métodos de maneira inteligente para evitar a necessidade de usar um operador ternário (? :
) ou uma declaração if / else.Meu exemplo embutido seria reescrito desta maneira:
Um exemplo meu que usa um método auxiliar seria reescrito desta maneira:
COMENTÁRIO
Vamos comparar as versões originais vs modificadas diretamente:
O original é uma abordagem direta, se for de trabalhador: obtemos um
Optional<Other>
; se tiver um valor, retornamos um fluxo contendo esse valor e, se não tiver valor, retornamos um fluxo vazio. Muito simples e fácil de explicar.A modificação é inteligente e tem a vantagem de evitar condicionais. (Eu sei que algumas pessoas não gostam do operador ternário. Se mal utilizado, pode realmente dificultar a compreensão do código.) No entanto, às vezes as coisas podem ser muito inteligentes. O código modificado também começa com um
Optional<Other>
. Em seguida, chama oOptional.map
que é definido da seguinte forma:A
map(Stream::of)
chamada retorna umOptional<Stream<Other>>
. Se um valor estava presente na entrada Opcional, o Opcional retornado contém um Fluxo que contém o único Outro resultado. Mas se o valor não estava presente, o resultado é um opcional vazio.Em seguida, a chamada para
orElseGet(Stream::empty)
retorna um valor de tipoStream<Other>
. Se o seu valor de entrada estiver presente, ele obtém o valor, que é o elemento únicoStream<Other>
. Caso contrário (se o valor de entrada estiver ausente), ele retornará um vazioStream<Other>
. Portanto, o resultado está correto, o mesmo que o código condicional original.Nos comentários discutindo minha resposta, sobre a edição rejeitada, eu descrevi essa técnica como "mais concisa, mas também mais obscura". Eu mantenho isso. Demorei um pouco para descobrir o que estava fazendo e também demorei um pouco para escrever a descrição acima do que estava fazendo. A sutileza principal é a transformação de
Optional<Other>
paraOptional<Stream<Other>>
. Uma vez que você grunhe, faz sentido, mas não foi óbvio para mim.Reconhecerei, porém, que coisas inicialmente obscuras podem se tornar idiomáticas ao longo do tempo. Pode ser que essa técnica acabe sendo a melhor maneira na prática, pelo menos até
Optional.stream
ser adicionada (se for o caso).UPDATE:
Optional.stream
foi adicionado ao JDK 9.fonte
Você não pode fazer isso de forma mais concisa, como você já está fazendo.
Você afirma que não quer
.filter(Optional::isPresent)
e.map(Optional::get)
.Isso foi resolvido pelo método que @StuartMarks descreve, no entanto, como resultado, você agora o mapeia para um
Optional<T>
, então agora você precisa usar.flatMap(this::streamopt)
e aget()
no final.Portanto, ele ainda consiste em duas instruções e agora você pode obter exceções com o novo método! Porque, e se cada opcional estiver vazio? Então o
findFirst()
retornará um opcional vazio e o seuget()
falhará!Então, o que você tem:
é realmente a melhor maneira de realizar o que você deseja, e é isso que você deseja salvar o resultado como um
T
, não como umOptional<T>
.Tomei a liberdade de criar uma
CustomOptional<T>
classe que envolvaOptional<T>
e forneça um método extraflatStream()
,. Observe que você não pode estenderOptional<T>
:Você verá que eu adicionei
flatStream()
, como aqui:Usado como:
Você ainda precisará retornar um
Stream<T>
aqui, pois não pode retornarT
, porque se!optional.isPresent()
, então,T == null
se você o declarar, mas você.flatMap(CustomOptional::flatStream)
tentaria adicionarnull
a um fluxo e isso não é possível.Como exemplo:
Usado como:
Agora lançará um
NullPointerException
dentro das operações de fluxo.Conclusão
O método que você usou é realmente o melhor método.
fonte
Uma versão um pouco mais curta usando
reduce
:Você também pode mover a função de redução para um método de utilitário estático e, em seguida, ela se torna:
fonte
Como minha resposta anterior parecia não ser muito popular, darei outra chance.
Uma resposta curta:
Você está principalmente no caminho certo. O código mais curto para obter a saída desejada que eu poderia criar é o seguinte:
Isso atenderá todos os seus requisitos:
Optional<Result>
this::resolve
preguiçosamente conforme necessáriothis::resolve
não será chamado após o primeiro resultado não vazioOptional<Result>
Resposta mais longa
A única modificação comparada à versão inicial do OP foi a remoção
.map(Optional::get)
antes da chamada.findFirst()
e adicionada.flatMap(o -> o)
como a última chamada na cadeia.Isso tem um bom efeito de se livrar do double-Optional, sempre que o fluxo encontrar um resultado real.
Você não pode realmente ficar mais curto que isso em Java.
O trecho de código alternativo usando a
for
técnica de loop mais convencional será sobre o mesmo número de linhas de código e terá mais ou menos a mesma ordem e número de operações que você precisa executar:this.resolve
,Optional.isPresent
Apenas para provar que minha solução funciona como anunciada, escrevi um pequeno programa de teste:
(Ele possui poucas linhas extras para depuração e verificação de que somente o número de chamadas a serem resolvidas é necessário ...)
Executando isso em uma linha de comando, obtive os seguintes resultados:
fonte
Se você não se importa em usar uma biblioteca de terceiros, pode usar o Javaslang . É como Scala, mas implementado em Java.
Ele vem com uma biblioteca de coleções imutável completa, muito semelhante à conhecida na Scala. Essas coleções substituem as coleções do Java e o Stream do Java 8. Ele também possui sua própria implementação do Option.
Aqui está uma solução para o exemplo da pergunta inicial:
Disclaimer: Eu sou o criador de Javaslang.
fonte
Tarde para a festa, mas e quanto a
Você pode se livrar do último get () se criar um método util para converter opcional em stream manualmente:
Se você retornar o fluxo imediatamente da sua função de resolução, salve mais uma linha.
fonte
Gostaria de promover métodos de fábrica para criar ajudantes para APIs funcionais:
O método de fábrica:
Raciocínio:
Como nas referências de método em geral, comparado às expressões lambda, você não pode capturar acidentalmente uma variável do escopo acessível, como:
t -> streamopt(resolve(o))
É compositável, você pode, por exemplo, chamar
Function::andThen
o resultado do método de fábrica:streamopt(this::resolve).andThen(...)
Considerando que, no caso de um lambda, você precisará lançá-lo primeiro:
((Function<T, Stream<R>>) t -> streamopt(resolve(t))).andThen(...)
fonte
Nulo é suportado pelo Stream fornecido Minha biblioteca AbacusUtil . Aqui está o código:
fonte
Se você está preso ao Java 8, mas tem acesso ao Guava 21.0 ou mais recente, pode usar
Streams.stream
para converter um opcional em um fluxo.Assim, dado
você pode escrever
fonte
Que tal?
https://stackoverflow.com/a/58281000/3477539
fonte
return list.stream().filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList()))
, assim como a pergunta (e sua resposta vinculada) tem ....get()
semisPresent()
, receberá um aviso no IntelliJProvavelmente você está fazendo errado.
O Java 8 opcional não se destina a ser usado dessa maneira. Geralmente, é reservado apenas para operações de fluxo de terminal que podem ou não retornar um valor, como localizar, por exemplo.
No seu caso, talvez seja melhor tentar primeiro encontrar uma maneira barata de filtrar os itens que podem ser resolvidos e, em seguida, obter o primeiro item como opcional e resolvê-lo como uma última operação. Melhor ainda - em vez de filtrar, localize o primeiro item resolvível e resolva-o.
A regra geral é que você deve se esforçar para reduzir o número de itens no fluxo antes de transformá-los em outra coisa. YMMV é claro.
fonte
Optional<Result> searchFor(Term t)
. Isso parece se encaixar na intenção de opcional. Além disso, stream () s deve ser avaliado preguiçosamente, para que não ocorra um trabalho extra de resolução de termos após o primeiro correspondente.