Java Language
Mappe
Ricerca…
introduzione
L' interfaccia java.util.Map rappresenta una mappatura tra le chiavi e i loro valori. Una mappa non può contenere chiavi duplicate; e ogni chiave può mappare al massimo un valore.
Poiché Map
è un'interfaccia, è necessario creare un'istanza concreta di tale interfaccia per utilizzarla; ci sono diverse implementazioni di Map
e usate principalmente java.util.HashMap
e java.util.TreeMap
Osservazioni
Una mappa è un oggetto che memorizza le chiavi con un valore associato per ogni chiave. Una chiave e il suo valore sono talvolta denominati coppia chiave / valore o una voce . Le mappe in genere forniscono queste funzionalità:
- I dati vengono memorizzati nella mappa in coppie chiave / valore.
- La mappa può contenere solo una voce per una particolare chiave. Se una mappa contiene una voce con una chiave particolare e si tenta di memorizzare una seconda voce con la stessa chiave, la seconda voce sostituirà la prima. In altre parole, questo cambierà il valore associato alla chiave.
- Le mappe forniscono operazioni veloci per verificare se esiste una chiave nella mappa, per recuperare il valore associato a una chiave e per rimuovere una coppia chiave / valore.
L'implementazione della mappa più comunemente utilizzata è HashMap . Funziona bene con chiavi che sono stringhe o numeri.
Mappe semplici come HashMap non sono ordinate. L'iterazione delle coppie chiave / valore può restituire singole voci in qualsiasi ordine. Se è necessario scorrere le voci della mappa in modo controllato, è necessario considerare quanto segue:
Mappe ordinate come TreeMap eseguiranno iterazioni attraverso le chiavi nel loro ordine naturale (o in un ordine che puoi specificare, fornendo un comparatore ). Ad esempio, una mappa ordinata utilizzando i numeri come chiavi dovrebbe scorrere le sue voci in ordine numerico.
LinkedHashMap consente di iterare le voci nello stesso ordine in cui sono state inserite nella mappa o dall'ordine di accesso più recente.
Aggiungi un elemento
- aggiunta
Map<Integer, String> map = new HashMap<>();
map.put(1, "First element.");
System.out.println(map.get(1));
Uscita: First element.
- Oltrepassare
Map<Integer, String> map = new HashMap<>();
map.put(1, "First element.");
map.put(1, "New element.");
System.out.println(map.get(1));
Uscita: New element.
HashMap
è usato come esempio. Possono essere utilizzate anche altre implementazioni che implementano l'interfaccia Map
.
Aggiungi più oggetti
Possiamo usare V put(K key,V value)
:
Associa il valore specificato con la chiave specificata in questa mappa (operazione opzionale). Se la mappa in precedenza conteneva un mapping per la chiave, il vecchio valore viene sostituito dal valore specificato.
String currentVal;
Map<Integer, String> map = new TreeMap<>();
currentVal = map.put(1, "First element.");
System.out.println(currentVal);// Will print null
currentVal = map.put(2, "Second element.");
System.out.println(currentVal); // Will print null yet again
currentVal = map.put(2, "This will replace 'Second element'");
System.out.println(currentVal); // will print Second element.
System.out.println(map.size()); // Will print 2 as key having
// value 2 was replaced.
Map<Integer, String> map2 = new HashMap<>();
map2.put(2, "Element 2");
map2.put(3, "Element 3");
map.putAll(map2);
System.out.println(map.size());
Produzione:
3
Per aggiungere molti elementi puoi usare una classe interna come questa:
Map<Integer, String> map = new HashMap<>() {{
// This is now an anonymous inner class with an unnamed instance constructor
put(5, "high");
put(4, "low");
put(1, "too slow");
}};
Tieni presente che la creazione di una classe interna anonima non è sempre efficiente e può portare a perdite di memoria, pertanto, quando possibile, utilizzare invece un blocco di inizializzazione:
static Map<Integer, String> map = new HashMap<>();
static {
// Now no inner classes are created so we can avoid memory leaks
put(5, "high");
put(4, "low");
put(1, "too slow");
}
L'esempio sopra rende la mappa statica. Può anche essere usato in un contesto non statico rimuovendo tutte le occorrenze di static
.
Oltre a ciò la maggior parte delle implementazioni supporta putAll
, che può aggiungere tutte le voci in una mappa a un'altra in questo modo:
another.putAll(one);
Utilizzo dei metodi predefiniti di Map da Java 8
Esempi di utilizzo di metodi predefiniti introdotti in Java 8 nell'interfaccia Mappa
- Utilizzando getOrDefault
Restituisce il valore mappato alla chiave, o se la chiave non è presente, restituisce il valore predefinito
Map<Integer, String> map = new HashMap<>();
map.put(1, "First element");
map.get(1); // => First element
map.get(2); // => null
map.getOrDefault(2, "Default element"); // => Default element
- Usando forEach
Permette di eseguire l'operazione specificata nell '"azione" su ogni voce della mappa
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
map.forEach((key, value) -> System.out.println("Key: "+key+ " :: Value: "+value));
// Key: 1 :: Value: one
// Key: 2 :: Value: two
// Key: 3 :: Value: three
- Utilizzando replaceAll
Sostituirà con nuovo valore solo se è presente la chiave
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("john", 20);
map.put("paul", 30);
map.put("peter", 40);
map.replaceAll((key,value)->value+10); //{john=30, paul=40, peter=50}
- Utilizzando putIfAbsent
La coppia valore-chiave viene aggiunta alla mappa, se la chiave non è presente o mappata su null
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("john", 20);
map.put("paul", 30);
map.put("peter", 40);
map.putIfAbsent("kelly", 50); //{john=20, paul=30, peter=40, kelly=50}
Usando rimuovere
Rimuove la chiave solo se è associata al valore specificato
Map<String, Integer> map = new HashMap<String, Integer>(); map.put("john", 20); map.put("paul", 30); map.put("peter", 40); map.remove("peter",40); //{john=30, paul=40}
Usando sostituire
Se la chiave è presente, il valore viene sostituito dal nuovo valore. Se la chiave non è presente, non fa nulla.
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("john", 20);
map.put("paul", 30);
map.put("peter", 40);
map.replace("peter",50); //{john=20, paul=30, peter=50}
map.replace("jack",60); //{john=20, paul=30, peter=50}
- Utilizzo di computeIfAbsent
Questo metodo aggiunge una voce nella mappa. la chiave è specificata nella funzione e il valore è il risultato dell'applicazione della funzione di mappatura
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("john", 20);
map.put("paul", 30);
map.put("peter", 40);
map.computeIfAbsent("kelly", k->map.get("john")+10); //{john=20, paul=30, peter=40, kelly=30}
map.computeIfAbsent("peter", k->map.get("john")+10); //{john=20, paul=30, peter=40, kelly=30} //peter already present
- Utilizzando computeIfPresent
Questo metodo aggiunge una voce o modifica una voce esistente nella mappa. Non fa nulla se non è presente una voce con quella chiave
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("john", 20);
map.put("paul", 30);
map.put("peter", 40);
map.computeIfPresent("kelly", (k,v)->v+10); //{john=20, paul=30, peter=40} //kelly not present
map.computeIfPresent("peter", (k,v)->v+10); //{john=20, paul=30, peter=50} // peter present, so increase the value
- Utilizzando il calcolo
Questo metodo sostituisce il valore di una chiave con il valore appena calcolato
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("john", 20);
map.put("paul", 30);
map.put("peter", 40);
map.compute("peter", (k,v)->v+50); //{john=20, paul=30, peter=90} //Increase the value
- Utilizzando l' unione
Aggiunge la coppia chiave-valore alla mappa, se la chiave non è presente o il valore per la chiave è null Sostituisce il valore con il valore appena calcolato, se la chiave è presente La chiave viene rimossa dalla mappa, se il nuovo valore calcolato è nullo
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("john", 20);
map.put("paul", 30);
map.put("peter", 40);
//Adds the key-value pair to the map, if key is not present or value for the key is null
map.merge("kelly", 50 , (k,v)->map.get("john")+10); // {john=20, paul=30, peter=40, kelly=50}
//Replaces the value with the newly computed value, if the key is present
map.merge("peter", 50 , (k,v)->map.get("john")+10); //{john=20, paul=30, peter=30, kelly=50}
//Key is removed from the map , if new value computed is null
map.merge("peter", 30 , (k,v)->map.get("nancy")); //{john=20, paul=30, kelly=50}
Cancella la mappa
Map<Integer, String> map = new HashMap<>();
map.put(1, "First element.");
map.put(2, "Second element.");
map.put(3, "Third element.");
map.clear();
System.out.println(map.size()); // => 0
Iterare attraverso il contenuto di una mappa
Maps fornisce metodi che consentono di accedere a chiavi, valori o coppie chiave-valore della mappa come raccolte. Puoi scorrere queste raccolte. Data la seguente mappa per esempio:
Map<String, Integer> repMap = new HashMap<>();
repMap.put("Jon Skeet", 927_654);
repMap.put("BalusC", 708_826);
repMap.put("Darin Dimitrov", 715_567);
Iterazione tramite chiavi della mappa:
for (String key : repMap.keySet()) {
System.out.println(key);
}
stampe:
Darin Dimitrov
Jon Skeet
BalusC
keySet()
fornisce le chiavi della mappa come un Set
. Set
viene utilizzato poiché le chiavi non possono contenere valori duplicati. L'iterazione attraverso l'insieme produce ogni chiave a turno. HashMaps non è ordinato, quindi in questo esempio le chiavi possono essere restituite in qualsiasi ordine.
Iterazione dei valori della mappa:
for (Integer value : repMap.values()) {
System.out.println(value);
}
stampe:
715.567
927654
708.826
values()
restituisce i valori della mappa come una Collection
. L'iterazione attraverso la raccolta produce a turno ogni valore. Di nuovo, i valori possono essere restituiti in qualsiasi ordine.
Iterazione tra chiavi e valori insieme
for (Map.Entry<String, Integer> entry : repMap.entrySet()) {
System.out.printf("%s = %d\n", entry.getKey(), entry.getValue());
}
stampe:
Darin Dimitrov = 715567
Jon Skeet = 927654
BalusC = 708826
entrySet()
restituisce una raccolta di oggetti Map.Entry
. Map.Entry dà accesso alla chiave e al valore per ogni voce.
Unione, combinazione e composizione di Maps
Usa putAll
per inserire tutti i membri di una mappa in un'altra. Le chiavi già presenti nella mappa avranno i loro valori corrispondenti sovrascritti.
Map<String, Integer> numbers = new HashMap<>();
numbers.put("One", 1)
numbers.put("Three", 3)
Map<String, Integer> other_numbers = new HashMap<>();
other_numbers.put("Two", 2)
other_numbers.put("Three", 4)
numbers.putAll(other_numbers)
Questo produce il seguente mapping in numbers
:
"One" -> 1
"Two" -> 2
"Three" -> 4 //old value 3 was overwritten by new value 4
Se si desidera combinare i valori anziché sovrascriverli, è possibile utilizzare Map.merge
, aggiunto in Java 8, che utilizza un BiFunction
fornito BiFunction
per unire i valori per le chiavi duplicate. merge
funziona su singole chiavi e valori, quindi dovrai utilizzare un loop o Map.forEach
. Qui concateniamo stringhe per chiavi duplicate:
for (Map.Entry<String, Integer> e : other_numbers.entrySet())
numbers.merge(e.getKey(), e.getValue(), Integer::sum);
//or instead of the above loop
other_numbers.forEach((k, v) -> numbers.merge(k, v, Integer::sum));
Se si desidera applicare il vincolo non ci sono chiavi duplicate, è possibile utilizzare una funzione di unione che genera un AssertionError
:
mapA.forEach((k, v) ->
mapB.merge(k, v, (v1, v2) ->
{throw new AssertionError("duplicate values for key: "+k);}));
Componi Map <X, Y> e Map <Y, Z> per ottenere la mappa <X, Z>
Se si desidera comporre due mapping, è possibile farlo come segue
Map<String, Integer> map1 = new HashMap<String, Integer>();
map1.put("key1", 1);
map1.put("key2", 2);
map1.put("key3", 3);
Map<Integer, Double> map2 = new HashMap<Integer, Double>();
map2.put(1, 1.0);
map2.put(2, 2.0);
map2.put(3, 3.0);
Map<String, Double> map3 = new new HashMap<String, Double>();
map1.forEach((key,value)->map3.put(key,map2.get(value)));
Questo produce la seguente mappatura
"key1" -> 1.0
"key2" -> 2.0
"key3" -> 3.0
Controlla se la chiave esiste
Map<String, String> num = new HashMap<>();
num.put("one", "first");
if (num.containsKey("one")) {
System.out.println(num.get("one")); // => first
}
Le mappe possono contenere valori nulli
Per le mappe, bisogna carrefull non confondere "contenente una chiave" con "avere un valore". Ad esempio, HashMap
può contenere null, il che significa che il seguente comportamento è perfettamente normale:
Map<String, String> map = new HashMap<>();
map.put("one", null);
if (map.containsKey("one")) {
System.out.println("This prints !"); // This line is reached
}
if (map.get("one") != null) {
System.out.println("This is never reached !"); // This line is never reached
}
Più formalmente, non vi è alcuna garanzia che map.contains(key) <=> map.get(key)!=null
Iterazione delle voci della mappa in modo efficiente
Questa sezione fornisce codice e benchmark per dieci implementazioni di esempio uniche che eseguono l'iterazione sulle voci di Map<Integer, Integer>
e generano la somma dei valori Integer
. Tutti gli esempi hanno una complessità algoritmica di Θ(n)
, tuttavia i benchmark sono ancora utili per fornire informazioni su quali implementazioni sono più efficienti in un ambiente "reale".
Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, Integer> pair = it.next();
sum += pair.getKey() + pair.getValue();
}
for (Map.Entry<Integer, Integer> pair : map.entrySet()) {
sum += pair.getKey() + pair.getValue();
}
- Implementazione con
Map.forEach
(Java 8+)
map.forEach((k, v) -> sum[0] += k + v);
- Implementazione utilizzando
Map.keySet
confor
for (Integer key : map.keySet()) {
sum += key + map.get(key);
}
- Implementazione tramite
Map.keySet
conIterator
Iterator<Integer> it = map.keySet().iterator();
while (it.hasNext()) {
Integer key = it.next();
sum += key + map.get(key);
}
for (Iterator<Map.Entry<Integer, Integer>> entries =
map.entrySet().iterator(); entries.hasNext(); ) {
Map.Entry<Integer, Integer> entry = entries.next();
sum += entry.getKey() + entry.getValue();
}
- Implementazione tramite
Stream.forEach
(Java 8+)
map.entrySet().stream().forEach(e -> sum += e.getKey() + e.getValue());
- Implementazione con
Stream.forEach
conStream.parallel
(Java 8+)
map.entrySet()
.stream()
.parallel()
.forEach(e -> sum += e.getKey() + e.getValue());
- Implementazione tramite
IterableMap
da Apache Collections
MapIterator<Integer, Integer> mit = iterableMap.mapIterator();
while (mit.hasNext()) {
sum += mit.next() + it.getValue();
}
- Implementazione con
MutableMap
da collezioni Eclipse
mutableMap.forEachKeyValue((key, value) -> {
sum += key + value;
});
Test delle prestazioni ( codice disponibile su Github )
Ambiente di test: Windows 8.1 a 64 bit, Intel i7-4790 3.60 GHz, 16 GB
Prestazioni medie di 10 test (100 elementi) Migliore: 308 ± 21 ns / op
Benchmark Score Error Units test3_UsingForEachAndJava8 308 ± 21 ns/op test10_UsingEclipseMutableMap 309 ± 9 ns/op test1_UsingWhileAndMapEntry 380 ± 14 ns/op test6_UsingForAndIterator 387 ± 16 ns/op test2_UsingForEachAndMapEntry 391 ± 23 ns/op test7_UsingJava8StreamAPI 510 ± 14 ns/op test9_UsingApacheIterableMap 524 ± 8 ns/op test4_UsingKeySetAndForEach 816 ± 26 ns/op test5_UsingKeySetAndIterator 863 ± 25 ns/op test8_UsingJava8StreamAPIParallel 5552 ± 185 ns/op
Prestazioni medie di 10 test (10000 elementi) Migliore: 37,606 ± 0,790 μs / op
Benchmark Score Error Units test10_UsingEclipseMutableMap 37606 ± 790 ns/op test3_UsingForEachAndJava8 50368 ± 887 ns/op test6_UsingForAndIterator 50332 ± 507 ns/op test2_UsingForEachAndMapEntry 51406 ± 1032 ns/op test1_UsingWhileAndMapEntry 52538 ± 2431 ns/op test7_UsingJava8StreamAPI 54464 ± 712 ns/op test4_UsingKeySetAndForEach 79016 ± 25345 ns/op test5_UsingKeySetAndIterator 91105 ± 10220 ns/op test8_UsingJava8StreamAPIParallel 112511 ± 365 ns/op test9_UsingApacheIterableMap 125714 ± 1935 ns/op
Prestazioni medie di 10 test (100.000 elementi) Migliore: 1184.767 ± 332.968 μs / op
Benchmark Score Error Units test1_UsingWhileAndMapEntry 1184.767 ± 332.968 μs/op test10_UsingEclipseMutableMap 1191.735 ± 304.273 μs/op test2_UsingForEachAndMapEntry 1205.815 ± 366.043 μs/op test6_UsingForAndIterator 1206.873 ± 367.272 μs/op test8_UsingJava8StreamAPIParallel 1485.895 ± 233.143 μs/op test5_UsingKeySetAndIterator 1540.281 ± 357.497 μs/op test4_UsingKeySetAndForEach 1593.342 ± 294.417 μs/op test3_UsingForEachAndJava8 1666.296 ± 126.443 μs/op test7_UsingJava8StreamAPI 1706.676 ± 436.867 μs/op test9_UsingApacheIterableMap 3289.866 ± 1445.564 μs/op
Un confronto delle variazioni delle prestazioni rispetto alle dimensioni della mappa
x: Size of Map
f(x): Benchmark Score (μs/op)
100 600 1100 1600 2100
---------------------------------------------------
10 | 0.333 1.631 2.752 5.937 8.024
3 | 0.309 1.971 4.147 8.147 10.473
6 | 0.372 2.190 4.470 8.322 10.531
1 | 0.405 2.237 4.616 8.645 10.707
Tests 2 | 0.376 2.267 4.809 8.403 10.910
f(x) 7 | 0.473 2.448 5.668 9.790 12.125
9 | 0.565 2.830 5.952 13.22 16.965
4 | 0.808 5.012 8.813 13.939 17.407
5 | 0.81 5.104 8.533 14.064 17.422
8 | 5.173 12.499 17.351 24.671 30.403
Usa oggetto personalizzato come chiave
Prima di utilizzare il proprio oggetto come chiave, è necessario sovrascrivere il metodo hashCode () e equals () dell'oggetto.
Nel caso semplice avresti qualcosa come:
class MyKey {
private String name;
MyKey(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof MyKey) {
return this.name.equals(((MyKey)obj).name);
}
return false;
}
@Override
public int hashCode() {
return this.name.hashCode();
}
}
hashCode
deciderà a quale hash bucket appartiene la chiave e equals
a decidere quale oggetto all'interno di quel bucket hash.
Senza questo metodo, il riferimento dell'oggetto verrà utilizzato per il confronto sopra che non funzionerà a meno che non si usi sempre lo stesso riferimento all'oggetto.
Utilizzo di HashMap
HashMap è un'implementazione dell'interfaccia Map che fornisce una struttura dati per archiviare i dati in coppie valore-chiave.
1. Dichiarazione di HashMap
Map<KeyType, ValueType> myMap = new HashMap<KeyType, ValueType>();
KeyType e ValueType devono essere tipi validi in Java, come ad esempio: String, Integer, Float o qualsiasi classe personalizzata come Employee, Student ecc.
Ad esempio: Map<String,Integer> myMap = new HashMap<String,Integer>();
2. Mettere i valori in HashMap.
Per mettere un valore nella HashMap, dobbiamo chiamare put
metodo sull'oggetto HashMap passando la chiave e il valore come parametri.
myMap.put("key1", 1);
myMap.put("key2", 2);
Se chiami il metodo put con la chiave che già esiste nella mappa, il metodo sostituirà il suo valore e restituirà il vecchio valore.
3. Ottenere valori da HashMap.
Per ottenere il valore da una HashMap devi chiamare il metodo get
, passando la chiave come parametro.
myMap.get("key1"); //return 1 (class Integer)
Se passi una chiave che non esiste in HashMap, questo metodo restituirà null
4. Controllare se la chiave è nella mappa o no.
myMap.containsKey(varKey);
5. Controlla se il valore è nella mappa o no.
myMap.containsValue(varValue);
I metodi precedenti restituiranno un valore boolean
vero o falso se la chiave, il valore esiste nella Mappa o no.
Creazione e inizializzazione di mappe
introduzione
Maps
memorizza coppie chiave / valore, in cui ogni chiave ha un valore associato. Data una chiave particolare, la mappa può cercare il valore associato molto rapidamente.
Maps
, noto anche come array associato, è un oggetto che memorizza i dati sotto forma di chiavi e valori. In Java, le mappe sono rappresentate tramite l'interfaccia Mappa che non è un'estensione dell'interfaccia di raccolta.
Modo 1: -
/*J2SE < 5.0*/ Map map = new HashMap(); map.put("name", "A"); map.put("address", "Malviya-Nagar"); map.put("city", "Jaipur"); System.out.println(map);
Via 2: -
/*J2SE 5.0+ style (use of generics):*/ Map<String, Object> map = new HashMap<>(); map.put("name", "A"); map.put("address", "Malviya-Nagar"); map.put("city", "Jaipur"); System.out.println(map);
Strada 3: -
Map<String, Object> map = new HashMap<String, Object>(){{ put("name", "A"); put("address", "Malviya-Nagar"); put("city", "Jaipur"); }}; System.out.println(map);
Strada 4: -
Map<String, Object> map = new TreeMap<String, Object>(); map.put("name", "A"); map.put("address", "Malviya-Nagar"); map.put("city", "Jaipur"); System.out.println(map);
Strada 5: -
//Java 8 final Map<String, String> map = Arrays.stream(new String[][] { { "name", "A" }, { "address", "Malviya-Nagar" }, { "city", "jaipur" }, }).collect(Collectors.toMap(m -> m[0], m -> m[1])); System.out.println(map);
Cammino 6: -
//This way for initial a map in outside the function final static Map<String, String> map; static { map = new HashMap<String, String>(); map.put("a", "b"); map.put("c", "d"); }
Way 7: - Creazione di una mappa di valore chiave immutabile.
//Immutable single key-value map Map<String, String> singletonMap = Collections.singletonMap("key", "value");
Si noti che è impossibile modificare tale mappa .
Qualsiasi tentativo di modificare la mappa comporterà il lancio di UnsupportedOperationException.
//Immutable single key-value pair Map<String, String> singletonMap = Collections.singletonMap("key", "value"); singletonMap.put("newKey", "newValue"); //will throw UnsupportedOperationException singletonMap.putAll(new HashMap<>()); //will throw UnsupportedOperationException singletonMap.remove("key"); //will throw UnsupportedOperationException singletonMap.replace("key", "value", "newValue"); //will throw UnsupportedOperationException //and etc