Bardzo chcę używać Map.computeIfAbsent, ale minęło zbyt dużo czasu od czasów lambda w undergrad.
Niemal bezpośrednio z dokumentacji: zawiera przykład starego sposobu robienia rzeczy:
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);
}
I nowy sposób:
map.computeIfAbsent(key, k -> new Value(f(k)));
Ale w ich przykładzie wydaje mi się, że nie całkiem „rozumiem”. Jak powinienem przekształcić kod, aby używał nowego sposobu wyrażania tego przez lambdę?
java
dictionary
lambda
java-8
Benjamin H.
źródło
źródło
Odpowiedzi:
Załóżmy, że masz następujący kod:
Wtedy zobaczysz wiadomość
creating a value for "snoop"
dokładnie raz, ponieważ przy drugim wywołaniucomputeIfAbsent
jest już wartość dla tego klucza. Wk
wyrażeniu lambdak -> f(k)
jest po prostu miejscem (parametr) dla klucza, który mapa przekaże do twojej lambdy w celu obliczenia wartości. W tym przykładzie klucz jest przekazywany do wywołania funkcji.Alternatywnie możesz napisać:
whoLetDogsOut.computeIfAbsent("snoop", k -> k.isEmpty());
aby osiągnąć ten sam wynik bez metody pomocniczej (ale wtedy nie zobaczysz wyniku debugowania). A nawet prostsze, ponieważ jest to prosta delegacja do istniejącej metody, którą można napisać:whoLetDogsOut.computeIfAbsent("snoop", String::isEmpty);
Ta delegacja nie wymaga zapisywania żadnych parametrów.Aby być bliżej przykładu w swoim pytaniu, możesz zapisać go jako
whoLetDogsOut.computeIfAbsent("snoop", key -> tryToLetOut(key));
(nie ma znaczenia, czy nazwiesz parametr,k
czykey
). Lub napisz to tak,whoLetDogsOut.computeIfAbsent("snoop", MyClass::tryToLetOut);
jakbytryToLetOut
byłostatic
lubwhoLetDogsOut.computeIfAbsent("snoop", this::tryToLetOut);
jeślitryToLetOut
jest metodą instancji.źródło
Ostatnio też bawiłem się tą metodą. Napisałem zapamiętany algorytm do obliczania liczb Fibonacciego, który mógłby posłużyć jako kolejna ilustracja tego, jak korzystać z metody.
Możemy zacząć od zdefiniowania mapę i umieszczenie w nim wartości dla bazy przypadkach, a mianowicie,
fibonnaci(0)
ifibonacci(1)
:A dla kroku indukcyjnego wszystko, co musimy zrobić, to przedefiniować naszą funkcję Fibonacciego w następujący sposób:
Jak widać, metoda
computeIfAbsent
wykorzysta podane wyrażenie lambda do obliczenia liczby Fibonacciego, gdy liczba ta nie jest obecna na mapie. Stanowi to znaczną poprawę w stosunku do tradycyjnego, rekurencyjnego algorytmu drzewiastego.źródło
HashMap
uszkodzenie wewnętrznych elementów, podobnie jak w bugs.openjdk.java.net/browse/JDK-8172951 i zawiedzieConcurrentModificationException
w Javie 9 ( bugs.openjdk.java.net/browse/JDK-8071667 )Inny przykład. Podczas budowania złożonej mapy map metoda computeIfAbsent () zastępuje metodę get () mapy. Poprzez łączenie wywołań computeIfAbsent () razem, brakujące kontenery są konstruowane w locie za pomocą podanych wyrażeń lambda:
źródło
wiele map
Jest to bardzo pomocne, jeśli chcesz utworzyć multimapę bez uciekania się do biblioteki Google Guava w celu jej implementacji
MultiMap
.Załóżmy na przykład, że chcesz przechowywać listę studentów, którzy zapisali się na określony przedmiot.
Normalnym rozwiązaniem tego problemu przy użyciu biblioteki JDK jest:
Ponieważ ma jakiś standardowy kod, ludzie zwykle używają guawy
Mutltimap
.Używając Map.computeIfAbsent, możemy pisać w jednej linii bez guawy Multimap w następujący sposób.
Stuart Marks i Brian Goetz zrobili dobrą rozmowę na ten temat https://www.youtube.com/watch?v=9uTVXxJjuco
źródło
studentListSubjectWise.stream().collect(Collectors.GroupingBy(subj::getSubjName, Collectors.toList());
tego po prostu. To tworzy multi-mapę typuMap<T,List<T>
w JDK, tylko bardziej zwięźle imho.