Java HashMap è una delle classi di Collection più popolari in Java. Java HashMap è un’implementazione basata su tabella hash. HashMap in Java estende la classe AbstractMap che implementa l’interfaccia Map.
Java HashMap
Alcuni dei punti importanti su HashMap in Java sono;
- Java HashMap consente la chiave nulla e i valori nulli.
- HashMap non è una raccolta ordinata. Puoi iterare sugli elementi di HashMap attraverso il set di chiavi, ma non è garantito che siano nell’ordine in cui sono stati aggiunti a HashMap.
- HashMap è quasi simile a Hashtable tranne che non è sincronizzata e consente chiavi e valori nulli.
- HashMap utilizza la sua classe interna Node<K,V> per memorizzare le voci della mappa.
- HashMap memorizza le voci in più liste collegate singolarmente, chiamate bucket o bin. Il numero predefinito di bin è 16 ed è sempre una potenza di 2.
- HashMap utilizza i metodi hashCode() e equals() sulle chiavi per le operazioni di get e put. Quindi, l’oggetto chiave di HashMap dovrebbe fornire una buona implementazione di questi metodi. Questa è la ragione per cui le classi immutabili sono più adatte per le chiavi, ad esempio String e Integer.
- Java HashMap non è thread-safe, per un ambiente multithreading dovresti utilizzare la classe ConcurrentHashMap o ottenere una mappa sincronizzata usando il metodo
Collections.synchronizedMap()
.
Costruttori di HashMap di Java
Java HashMap fornisce quattro costruttori.
- public HashMap(): Costruttore HashMap più comunemente usato. Questo costruttore creerà un HashMap vuoto con capacità iniziale predefinita di 16 e fattore di carico 0,75
- public HashMap(int initialCapacity): Questo costruttore di HashMap viene utilizzato per specificare la capacità iniziale e il fattore di carico 0,75. Questo è utile per evitare il ri-hashing se si conosce il numero di mapping da memorizzare nell’HashMap.
- public HashMap(int initialCapacity, float loadFactor): Questo costruttore di HashMap creerà un HashMap vuoto con capacità iniziale e fattore di carico specificati. Puoi utilizzarlo se conosci il numero massimo di mapping da memorizzare nell’HashMap. In scenari comuni dovresti evitarlo perché il fattore di carico 0,75 offre un buon compromesso tra spazio e costo temporale.
- public HashMap(Map<? extends K, ? extends V> m): Crea una mappa con gli stessi mapping della mappa specificata e con fattore di carico 0,75
Esempio di Costruttori di HashMap di Java
Il frammento di codice sottostante mostra un esempio di HashMap che utilizza tutti i costruttori sopra.
Map<String, String> map1 = new HashMap<>();
Map<String, String> map2 = new HashMap<>(2^5);
Map<String, String> map3 = new HashMap<>(32,0.80f);
Map<String,String> map4 = new HashMap<>(map1);
Metodi di HashMap di Java
Diamo un’occhiata ai metodi importanti di HashMap in Java.
- public void clear(): Questo metodo di HashMap rimuoverà tutte le associazioni e la HashMap diventerà vuota.
- public boolean containsKey(Object key): Questo metodo restituirà ‘true’ se la chiave esiste, altrimenti restituirà ‘false’.
- public boolean containsValue(Object value): Questo metodo di HashMap restituisce true se il valore esiste, altrimenti false.
- public Set<Map.Entry<K,V>> entrySet(): Questo metodo restituisce una vista Set delle associazioni di HashMap. Questo set è supportato dalla mappa, quindi le modifiche alla mappa si riflettono nel set e viceversa.
- public V get(Object key): Restituisce il valore associato alla chiave specificata, o null se non c’è alcuna associazione per la chiave.
- public boolean isEmpty(): Un metodo di utilità che restituisce true se non ci sono associazioni chiave-valore presenti.
- public Set<K> keySet(): Restituisce una vista Set delle chiavi contenute in questa mappa. Il set è supportato dalla mappa, quindi le modifiche alla mappa si riflettono nel set e viceversa.
- public V put(K key, V value): Associa il valore specificato alla chiave specificata in questa mappa. Se la mappa conteneva precedentemente un’associazione per la chiave, il vecchio valore viene sostituito.
- public void putAll(Map extends K, ? extends V> m): Copia tutte le associazioni dalla mappa specificata a questa mappa. Queste associazioni sostituiranno eventuali associazioni che questa mappa aveva per una qualsiasi delle chiavi attualmente nella mappa specificata.
- public V remove(Object key): Rimuove l’associazione per la chiave specificata da questa mappa, se presente.
- public int size(): Restituisce il numero di associazioni chiave-valore in questa mappa.
- public Collection
values() : Restituisce una vista Collection dei valori contenuti in questa mappa. La collezione è supportata dalla mappa, quindi le modifiche alla mappa si riflettono nella collezione, e viceversa.
Vi sono molti nuovi metodi in HashMap introdotti in Java 8.
- public V computeIfAbsent(K key, Function super K, ? extends V> mappingFunction): Se la chiave specificata non è già associata a un valore (o è mappata su null), questo metodo cerca di calcolare il suo valore utilizzando la funzione di mapping fornita e lo inserisce nella HashMap a meno che sia Null.
- public V computeIfPresent(K key, BiFunction super K, ? super V, ? extends V> remappingFunction): Se il valore per la chiave specificata è presente e non nullo, cerca di calcolare una nuova mappatura data la chiave e il suo valore mappato corrente.
- public V compute(K key, BiFunction super K, ? super V, ? extends V> remappingFunction): Questo metodo di HashMap cerca di calcolare una mappatura per la chiave specificata e il suo valore mappato corrente.
- public void forEach(BiConsumer super K, ? super V> action): Questo metodo esegue l’azione data per ogni voce in questa mappa.
- public V getOrDefault(Object key, V defaultValue): Uguale a get tranne che defaultValue viene restituito se non viene trovata alcuna mappatura per la chiave specificata.
- public V merge(K key, V value, BiFunction super V, ? super V, ? extends V> remappingFunction): Se la chiave specificata non è già associata a un valore o è associata a null, la associa con il valore non null dato. In caso contrario, sostituisce il valore associato con i risultati della funzione di rimappatura data o rimuove se il risultato è null.
- public V putIfAbsent(K key, V value): Se la chiave specificata non è già associata a un valore (o è mappata a null) la associa con il valore dato e restituisce null, altrimenti restituisce il valore attuale.
- public boolean remove(Object key, Object value): Rimuove l’entrata per la chiave specificata solo se attualmente è mappata al valore specificato.
- public boolean replace(K key, V oldValue, V newValue): Sostituisce l’entrata per la chiave specificata solo se attualmente è mappata al valore specificato.
- public V replace(K key, V value): Sostituisce l’entrata per la chiave specificata solo se attualmente è mappata a un certo valore.
- public void replaceAll(BiFunction super K, ? super V, ? extends V> function): Sostituisce il valore di ogni voce con il risultato dell’invocazione della funzione data su quella voce.
Esempio di HashMap Java
Ecco un semplice programma Java per i metodi comunemente usati di HashMap.
package com.journaldev.examples;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class HashMapExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1", "1"); // put example
map.put("2", "2");
map.put("3", "3");
map.put("4", null); // null value
map.put(null, "100"); // null key
String value = map.get("3"); // get example
System.out.println("Key = 3, Value = " + value);
value = map.getOrDefault("5", "Default Value");
System.out.println("Key = 5, Value=" + value);
boolean keyExists = map.containsKey(null);
boolean valueExists = map.containsValue("100");
System.out.println("keyExists=" + keyExists + ", valueExists=" + valueExists);
Set<Entry<String, String>> entrySet = map.entrySet();
System.out.println(entrySet);
System.out.println("map size=" + map.size());
Map<String, String> map1 = new HashMap<>();
map1.putAll(map);
System.out.println("map1 mappings= " + map1);
String nullKeyValue = map1.remove(null);
System.out.println("map1 null key value = " + nullKeyValue);
System.out.println("map1 after removing null key = " + map1);
Set<String> keySet = map.keySet();
System.out.println("map keys = " + keySet);
Collection<String> values = map.values();
System.out.println("map values = " + values);
map.clear();
System.out.println("map is empty=" + map.isEmpty());
}
}
Di seguito è riportato l’output del programma di esempio di HashMap Java sopra.
Key = 3, Value = 3
Key = 5, Value=Default Value
keyExists=true, valueExists=true
[null=100, 1=1, 2=2, 3=3, 4=null]
map size=5
map1 mappings= {null=100, 1=1, 2=2, 3=3, 4=null}
map1 null key value = 100
map1 after removing null key = {1=1, 2=2, 3=3, 4=null}
map keys = [null, 1, 2, 3, 4]
map values = [100, 1, 2, 3, null]
map is empty=true
Come funziona HashMap in Java?
HashMap in Java utilizza la sua classe interna Node<K, V> per memorizzare i mapping. HashMap funziona su un algoritmo di hash e utilizza i metodi hashCode() e equals() sulla chiave per le operazioni di get e put. HashMap utilizza una lista collegata singolarmente per memorizzare gli elementi, chiamati contenitori o secchielli. Quando chiamiamo il metodo put, viene utilizzato l’hashCode della chiave per determinare il secchiello che verrà utilizzato per memorizzare il mapping. Una volta identificato il secchiello, viene utilizzato l’hashCode per verificare se esiste già una chiave con lo stesso hashCode o meno. Se esiste già una chiave esistente con lo stesso hashCode, allora il metodo equals() viene utilizzato sulla chiave. Se equals restituisce true, il valore viene sovrascritto, altrimenti viene creato un nuovo mapping in questo secchiello di lista collegata singolarmente. Se non c’è una chiave con lo stesso hashCode, allora il mapping viene inserito nel secchiello. Per l’operazione get di HashMap, nuovamente l’hashCode della chiave viene utilizzato per determinare il secchiello in cui cercare il valore. Dopo che il secchiello è identificato, gli elementi vengono attraversati per trovare l’Entry utilizzando il metodo hashCode e equals. Se viene trovata una corrispondenza, viene restituito il valore altrimenti viene restituito null. Ci sono molto più cose coinvolte, come l’algoritmo di hash per ottenere il secchiello per la chiave, il ri-hashing dei mapping, ecc. Ma per il nostro lavoro, ricorda solo che le operazioni di HashMap funzionano sulla chiave e una buona implementazione dei metodi hashCode e equals è necessaria per evitare comportamenti indesiderati. L’immagine seguente mostra l’illustrazione delle operazioni di get e put. Lettura consigliata: Importanza dei metodi hashCode e equals in Java
Java HashMap Load Factor
Il Load Factor è utilizzato per determinare quando la HashMap verrà riassegnata e la dimensione del bucket sarà aumentata. Il valore predefinito del bucket o della capacità è 16 e il load factor è 0,75. La soglia per la riassegnazione viene calcolata moltiplicando la capacità per il load factor. Quindi il valore predefinito della soglia sarà 12. Quindi, quando la HashMap avrà più di 12 mapping, verrà riassegnata e il numero di bin verrà aumentato alla potenza successiva di 2, ovvero 32. Nota che la capacità della HashMap è sempre una potenza di 2. Il load factor predefinito del 0,75 fornisce un buon compromesso tra spazio e complessità temporale. Tuttavia, è possibile impostarlo su valori diversi in base alle proprie esigenze. Se si desidera risparmiare spazio, è possibile aumentare il suo valore a 0,80 o 0,90, ma le operazioni di get/put richiederanno più tempo.
Java HashMap keySet
Il metodo keySet di Java HashMap restituisce la vista Set delle chiavi nella HashMap. Questa vista Set è supportata dalla HashMap e qualsiasi modifica nella HashMap si riflette nel Set e viceversa. Di seguito è riportato un semplice programma che illustra esempi di keySet di HashMap e qual è il modo giusto se si desidera un keySet non supportato dalla mappa.
package com.journaldev.examples;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class HashMapKeySetExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put("3", "3");
Set<String> keySet = map.keySet();
System.out.println(keySet);
map.put("4", "4");
System.out.println(keySet); // keySet is backed by Map
keySet.remove("1");
System.out.println(map); // map is also modified
keySet = new HashSet<>(map.keySet()); // copies the key to new Set
map.put("5", "5");
System.out.println(keySet); // keySet is not modified
}
}
L’output del programma sopra chiarirà che il keySet è supportato dalla mappa.
[1, 2, 3]
[1, 2, 3, 4]
{2=2, 3=3, 4=4}
[2, 3, 4]
Valori di HashMap di Java
Il metodo dei valori di HashMap di Java restituisce una vista di Collection dei valori nella mappa. Questa collezione è supportata da HashMap, quindi qualsiasi modifica in HashMap si rifletterà nella collezione dei valori e viceversa. Un semplice esempio qui sotto conferma questo comportamento della collezione dei valori di HashMap.
package com.journaldev.examples;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class HashMapValuesExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put("3", null);
map.put("4", null);
map.put(null, "100");
Collection<String> values = map.values();
System.out.println("map values = " + values);
map.remove(null);
System.out.println("map values after removing null key = " + values);
map.put("5", "5");
System.out.println("map values after put = " + values);
System.out.println(map);
values.remove("1"); // changing values collection
System.out.println(map); // updates in map too
}
}
Di seguito è riportato l’output del programma sopra.
map values = [100, 1, 2, null, null]
map values after removing null key = [1, 2, null, null]
map values after put = [1, 2, null, null, 5]
{1=1, 2=2, 3=null, 4=null, 5=5}
{2=2, 3=null, 4=null, 5=5}
Insieme di voci di HashMap di Java
Il metodo entrySet di HashMap di Java restituisce la vista Set delle mappature. Questo entrySet è supportato da HashMap, quindi qualsiasi modifica nella mappa si riflette nell’insieme di voci e viceversa. Dai un’occhiata al programma di esempio qui sotto per un esempio di entrySet di HashMap.
package com.journaldev.examples;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class HashMapEntrySetExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1", "1");
map.put("2", null);
map.put(null, "100");
Set<Entry<String,String>> entrySet = map.entrySet();
Iterator<Entry<String, String>> iterator = entrySet.iterator();
Entry<String, String> next = null;
System.out.println("map before processing = "+map);
System.out.println("entrySet before processing = "+entrySet);
while(iterator.hasNext()){
next = iterator.next();
System.out.println("Processing on: "+next.getValue());
if(next.getKey() == null) iterator.remove();
}
System.out.println("map after processing = "+map);
System.out.println("entrySet after processing = "+entrySet);
Entry<String, String> simpleEntry = new AbstractMap.SimpleEntry<String, String>("1","1");
entrySet.remove(simpleEntry);
System.out.println("map after removing Entry = "+map);
System.out.println("entrySet after removing Entry = "+entrySet);
}
}
Di seguito è riportato l’output prodotto dal programma sopra.
map before processing = {null=100, 1=1, 2=null}
entrySet before processing = [null=100, 1=1, 2=null]
Processing on: 100
Processing on: 1
Processing on: null
map after processing = {1=1, 2=null}
entrySet after processing = [1=1, 2=null]
map after removing Entry = {2=null}
entrySet after removing Entry = [2=null]
Metodo putIfAbsent di HashMap di Java
A simple example for HashMap putIfAbsent method introduced in Java 8.
package com.journaldev.examples;
import java.util.HashMap;
import java.util.Map;
public class HashMapPutIfAbsentExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1", "1");
map.put("2", null);
map.put(null, "100");
System.out.println("map before putIfAbsent = "+map);
String value = map.putIfAbsent("1", "4");
System.out.println("map after putIfAbsent = "+map);
System.out.println("putIfAbsent returns: "+value);
System.out.println("map before putIfAbsent = "+map);
value = map.putIfAbsent("3", "3");
System.out.println("map after putIfAbsent = "+map);
System.out.println("putIfAbsent returns: "+value);
}
}
L’output del programma sopra è il seguente;
map before putIfAbsent = {null=100, 1=1, 2=null}
map after putIfAbsent = {null=100, 1=1, 2=null}
putIfAbsent returns: 1
map before putIfAbsent = {null=100, 1=1, 2=null}
map after putIfAbsent = {null=100, 1=1, 2=null, 3=3}
putIfAbsent returns: null
ForEach di HashMap di Java
Il metodo forEach di HashMap è introdotto in Java 8. È un metodo molto utile per eseguire l’azione data per ogni voce nella mappa fino a quando tutte le voci non sono state elaborate o l’azione genera un’eccezione.
package com.journaldev.examples;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
public class HashMapForEachExample {
public static void main(String[] args) {
Map map = new HashMap<>();
map.put("1", "1");
map.put("2", null);
map.put(null, "100");
BiConsumer action = new MyBiConsumer();
map.forEach(action);
//esempio di espressione lambda
System.out.println("\nHashMap forEach lambda example\n");
map.forEach((k,v) -> {System.out.println("Key = "+k+", Value = "+v);});
}
}
class MyBiConsumer implements BiConsumer {
@Override
public void accept(String t, String u) {
System.out.println("Key = " + t);
System.out.println("Processing on value = " + u);
}
}
L’output del programma di esempio precedente usando il metodo forEach di HashMap è;
Key = null
Processing on value = 100
Key = 1
Processing on value = 1
Key = 2
Processing on value = null
HashMap forEach lambda example
Key = null, Value = 100
Key = 1, Value = 1
Key = 2, Value = null
Java HashMap replaceAll
Il metodo replaceAll di HashMap può essere utilizzato per sostituire il valore di ogni voce con il risultato dell’invocazione della funzione data su quella voce. Questo metodo è aggiunto in Java 8 e possiamo utilizzare espressioni lambda per questo argomento del metodo.
package com.journaldev.examples;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
public class HashMapReplaceAllExample {
public static void main(String[] args) {
Map map = new HashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put(null, "100");
System.out.println("map before replaceAll = " + map);
BiFunction function = new MyBiFunction();
map.replaceAll(function);
System.out.println("map after replaceAll = " + map);
// sostituisci tutto usando espressioni lambda
map.replaceAll((k, v) -> {
if (k != null) return k + v;
else return v;});
System.out.println("map after replaceAll lambda expression = " + map);
}
}
class MyBiFunction implements BiFunction {
@Override
public String apply(String t, String u) {
if (t != null)
return t + u;
else
return u;
}
}
L’output del programma di sostituzione di tutte le voci precedente di HashMap è;
map before replaceAll = {null=100, 1=1, 2=2}
map after replaceAll = {null=100, 1=11, 2=22}
map after replaceAll lambda example = {null=100, 1=111, 2=222}
Java HashMap computeIfAbsent
Il metodo computeIfAbsent di HashMap calcola il valore solo se la chiave non è presente nella mappa. Dopo aver calcolato il valore, viene inserito nella mappa se non è nullo.
package com.journaldev.examples;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
public class HashMapComputeIfAbsent {
public static void main(String[] args) {
Map map = new HashMap<>();
map.put("1", "10");
map.put("2", "20");
map.put(null, "100");
Function function = new MyFunction();
map.computeIfAbsent("3", function); //key not present
map.computeIfAbsent("2", function); //key already present
//modo lambda
map.computeIfAbsent("4", v -> {return v;});
map.computeIfAbsent("5", v -> {return null;}); //null value won't get inserted
System.out.println(map);
}
}
class MyFunction implements Function {
@Override
public String apply(String t) {
return t;
}
}
L’output del programma precedente è;
{null=100, 1=10, 2=20, 3=3, 4=4}
Java HashMap computeIfPresent
Il metodo computeIfPresent di Java HashMap ricomputa il valore se la chiave specificata è presente e il valore non è nullo. Se la funzione restituisce null, il mapping viene rimosso.
package com.journaldev.examples;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
public class HashMapComputeIfPresentExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1", "10");
map.put("2", "20");
map.put(null, "100");
map.put("10", null);
System.out.println("map before computeIfPresent = " + map);
BiFunction<String, String, String> function = new MyBiFunction1();
for (String key : map.keySet()) {
map.computeIfPresent(key, function);
}
System.out.println("map after computeIfPresent = " + map);
map.computeIfPresent("1", (k,v) -> {return null;}); // mapping will be removed
System.out.println("map after computeIfPresent = " + map);
}
}
class MyBiFunction1 implements BiFunction<String, String, String> {
@Override
public String apply(String t, String u) {
return t + u;
}
}
L’output prodotto dall’esempio di HashMap computeIfPresent è;
map before computeIfPresent = {null=100, 1=10, 2=20, 10=null}
map after computeIfPresent = {null=null100, 1=110, 2=220, 10=null}
map after computeIfPresent = {null=null100, 2=220, 10=null}
Java HashMap compute
Se si desidera applicare una funzione a tutti i mapping in base alla chiave e al valore, allora dovrebbe essere utilizzato il metodo compute. Se non c’è alcun mapping e viene utilizzato questo metodo, il valore sarà nullo per la funzione compute.
package com.journaldev.examples;
import java.util.HashMap;
import java.util.Map;
public class HashMapComputeExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put(null, "10");
map.put("10", null);
System.out.println("map before compute = "+map);
for (String key : map.keySet()) {
map.compute(key, (k,v) -> {return k+v;});
}
map.compute("5", (k,v) -> {return k+v;}); //key not present, v = null
System.out.println("map after compute = "+map);
}
}
L’output dell’esempio di HashMap compute è;
map before compute = {null=10, 1=1, 2=2, 10=null}
map after compute = {null=null10, 1=11, 2=22, 5=5null, 10=10null}
Java HashMap merge
Se la chiave specificata non è presente o è associata a null, allora la associa al dato valore non nullo fornito. Altrimenti, sostituisce il valore associato con i risultati della data funzione di riassegnazione, o rimuove se il risultato è null.
package com.journaldev.examples;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
public class HashMapMergeExample {
public static void main(String[] args) {
Map map = new HashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put(null, "10");
map.put("10", null);
for (Entry entry : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
//merge lancia NullPointerException se la chiave o il valore è null
if(key != null && value != null)
map.merge(entry.getKey(), entry.getValue(),
(k, v) -> {return k + v;});
}
System.out.println(map);
map.merge("5", "5", (k, v) -> {return k + v;}); // key not present
System.out.println(map);
map.merge("1", "1", (k, v) -> {return null;}); // method return null, so remove
System.out.println(map);
}
}
L’output del programma sopra è;
{null=10, 1=11, 2=22, 10=null}
{null=10, 1=11, 2=22, 5=5, 10=null}
{null=10, 2=22, 5=5, 10=null}
Ecco tutto per HashMap in Java, spero che non sia sfuggito nulla di importante. Condividilo anche con gli altri se ti è piaciuto. Riferimento: API Doc
Source:
https://www.digitalocean.com/community/tutorials/java-hashmap