Eu gostaria muito de usar Map.computeIfAbsent, mas já faz muito tempo que lambdas na graduação.
Quase diretamente dos documentos: dá um exemplo da velha maneira de fazer as coisas:
Map<String, Boolean> whoLetDogsOut = new ConcurrentHashMap<>();
String key = "snoop";
if (whoLetDogsOut.get(key) == null) {
Boolean isLetOut = tryToLetOut(key);
if (isLetOut != null)
map.putIfAbsent(key, isLetOut);
}
E a nova forma:
map.computeIfAbsent(key, k -> new Value(f(k)));
Mas, no exemplo deles, acho que não estou "entendendo". Como eu transformaria o código para usar a nova forma lambda de expressar isso?
java
dictionary
lambda
java-8
Benjamin H
fonte
fonte
Respostas:
Suponha que você tenha o seguinte código:
Então você verá a mensagem
creating a value for "snoop"
exatamente uma vez, pois na segunda invocação decomputeIfAbsent
já existe um valor para aquela chave. Ok
na expressão lambdak -> f(k)
é apenas um placeolder (parâmetro) para a chave que o mapa passará para seu lambda para calcular o valor. Portanto, no exemplo, a chave é passada para a invocação da função.Alternativamente, você pode escrever:
whoLetDogsOut.computeIfAbsent("snoop", k -> k.isEmpty());
para obter o mesmo resultado sem um método auxiliar (mas você não verá a saída de depuração então). E ainda mais simples, pois é uma delegação simples para um método existente que você pode escrever:whoLetDogsOut.computeIfAbsent("snoop", String::isEmpty);
Esta delegação não precisa de nenhum parâmetro para ser escrito.Para ficar mais próximo do exemplo em sua pergunta, você pode escrever como
whoLetDogsOut.computeIfAbsent("snoop", key -> tryToLetOut(key));
(não importa se você nomeia o parâmetrok
oukey
). Ou escreva comowhoLetDogsOut.computeIfAbsent("snoop", MyClass::tryToLetOut);
setryToLetOut
fossestatic
ouwhoLetDogsOut.computeIfAbsent("snoop", this::tryToLetOut);
setryToLetOut
fosse um método de instância.fonte
Recentemente, também comecei a usar esse método. Eu escrevi um algoritmo memoized para calcular os números de Fibonacci que podem servir como outra ilustração sobre como usar o método.
Podemos começar definindo um mapa e colocando os valores nele para os casos base, a saber,
fibonnaci(0)
efibonacci(1)
:E para a etapa indutiva, tudo o que temos a fazer é redefinir nossa função Fibonacci da seguinte maneira:
Como você pode ver, o método
computeIfAbsent
usará a expressão lambda fornecida para calcular o número de Fibonacci quando o número não estiver presente no mapa. Isso representa uma melhoria significativa em relação ao algoritmo tradicional de árvore recursiva.fonte
HashMap
corrupção dos internos de, assim como em bugs.openjdk.java.net/browse/JDK-8172951 e irá falhar comConcurrentModificationException
no Java 9 ( bugs.openjdk.java.net/browse/JDK-8071667 )Outro exemplo. Ao construir um mapa complexo de mapas, o método computeIfAbsent () substitui o método get () do mapa. Por meio do encadeamento de chamadas computeIfAbsent (), os contêineres ausentes são construídos instantaneamente pelas expressões lambda fornecidas:
fonte
multi-mapa
Isso é realmente útil se você deseja criar um multimapa sem recorrer à biblioteca Google Guava para a implementação de
MultiMap
.Por exemplo, suponha que você queira armazenar uma lista de alunos que se inscreveram em um determinado assunto.
A solução normal para isso usando a biblioteca JDK é:
Por ter algum código padrão, as pessoas tendem a usar Guava
Mutltimap
.Usando Map.computeIfAbsent, podemos escrever em uma única linha sem Guava Multimap como segue.
Stuart Marks e Brian Goetz fizeram uma boa palestra sobre isso https://www.youtube.com/watch?v=9uTVXxJjuco
fonte
studentListSubjectWise.stream().collect(Collectors.GroupingBy(subj::getSubjName, Collectors.toList());
Isso produz um multimapa do tipoMap<T,List<T>
em JDK, mas de forma mais concisa imho.