Ce face ConcurrentHashMap.merge
merge(cheie, valoare, combina): dacă cheia nu există, pune valoare; dacă există, pune combina(valoareVeche, valoare). Cu Integer::sum obții un contor: harta.merge(cuvant, 1, Integer::sum).
compute(cheie, (k, vechi) -> nou) primește cheia și valoarea veche (null dacă lipsește) și returnează valoarea nouă. Dacă funcția returnează null, cheia este ștearsă.
În ConcurrentHashMap, ambele metode sunt atomice: niciun alt fir nu poate modifica aceeași cheie în timpul calculului, deci nu se pierd actualizări. Funcția trebuie să fie scurtă și să nu modifice alte chei ale aceleiași hărți.
Parametri
| Parametru | Descriere |
|---|---|
cheie |
Cheia actualizată. |
valoare |
Pentru merge: valoarea pusă dacă cheia lipsește și al doilea argument pentru combina. |
combina / functie |
Calculează valoarea nouă; null înseamnă ștergerea cheii. |
Ce returnează
Valoarea nouă a cheii (sau null dacă a fost ștearsă).
Exemple
Exemplul 1: Numărarea voturilor din mai multe fire
Trei fire numără voturi în aceeași hartă. Cu merge nu se pierde niciun vot.
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
String[] candidati = {"Ana", "Bogdan", "Carmen"};
Map<String, Integer> voturi = new ConcurrentHashMap<>();
try (ExecutorService sectii = Executors.newFixedThreadPool(3)) {
for (int s = 0; s < 3; s++) {
sectii.submit(() -> {
for (int i = 0; i < 3000; i++) {
voturi.merge(candidati[i % 3], 1, Integer::sum);
}
});
}
}
System.out.println(new TreeMap<>(voturi));
}
}
Rezultat
{Ana=3000, Bogdan=3000, Carmen=3000}
Exemplul 2: compute: stocul scade și cheia dispare la zero
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
public class Main {
static void vinde(Map<String, Integer> stoc, String produs) {
stoc.compute(produs, (p, bucati) -> bucati == null || bucati <= 1 ? null : bucati - 1);
}
public static void main(String[] args) {
Map<String, Integer> stoc = new ConcurrentHashMap<>(Map.of("caiet", 3, "pix", 1));
vinde(stoc, "caiet");
vinde(stoc, "pix");
System.out.println(new TreeMap<>(stoc));
stoc.compute("creion", (p, bucati) -> bucati == null ? 10 : bucati + 10);
System.out.println(new TreeMap<>(stoc));
}
}
Rezultat
{caiet=2}
{caiet=2, creion=10}
Exemplul 3: merge cu text: concatenare
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
public class Main {
public static void main(String[] args) {
Map<String, String> materii = new ConcurrentHashMap<>();
materii.merge("Ana", "mate", (a, b) -> a + ", " + b);
materii.merge("Ana", "română", (a, b) -> a + ", " + b);
materii.merge("Mihai", "fizică", (a, b) -> a + ", " + b);
System.out.println(new TreeMap<>(materii));
}
}
Rezultat
{Ana=mate, română, Mihai=fizică}
Greșeli frecvente
- Faci
get, apoiputpentru un contor. Între cele două operații alt fir poate scrie;mergeface totul atomic. - Din funcția lui
computemodifici altă cheie a aceleiași hărți. Poate bloca programul sau aruncaIllegalStateException.
Comenzi înrudite
ConcurrentHashMap: Un Map pe care mai multe fire îl pot citi și modifica în același timp, fără să se strice datele.AtomicInteger.incrementAndGet: Crește atomic valoarea cu 1: incrementAndGet() returnează valoarea nouă (++x), getAndIncrement() pe cea veche (x++).AtomicInteger: Un int pe care mai multe fire îl pot modifica sigur, fără synchronized: get(), set(), addAndGet() și altele.