Java HashMap 是 Java 中最受歡迎的集合類之一。Java HashMap 是基於哈希表的實現。HashMap 在 Java 中擴展了 AbstractMap 類,該類實現了 Map 接口。
Java HashMap
有關 Java 中 HashMap 的一些重要信息是:
- Java HashMap 允許 null 鍵和 null 值。
- HashMap 不是有序集合。您可以通過鍵集遍歷 HashMap 条目,但不能保證它們按照添加到 HashMap 的順序排列。
- HashMap 幾乎與 Hashtable 相似,只是它是非同步的,並允許 null 鍵和值。
- HashMap 使用它的內部類 Node
來存儲映射條目。 - HashMap 將條目存儲到多個單鏈表中,稱為桶或箱。默認的桶數量是 16,並且始終是 2 的冪次方。
- HashMap 在 get 和 put 操作中使用鍵的 hashCode() 和 equals() 方法。因此,HashMap 鍵對象應該提供這些方法的良好實現。這就是為什麼不可變類更適合用作鍵的原因,例如 String 和 Integer。
- Java HashMap 不是線程安全的,對於多線程環境,您應該使用 ConcurrentHashMap 類或使用
Collections.synchronizedMap()
方法獲取同步的映射。
Java HashMap 构造函数
Java HashMap 提供四个构造函数。
- public HashMap(): 最常用的 HashMap 构造函数。此构造函数将创建一个空的 HashMap,具有默认初始容量16和负载因子0.75。
- public HashMap(int initialCapacity): 此 HashMap 构造函数用于指定初始容量和0.75的负载因子。如果您知道要存储在 HashMap 中的映射数目,可以使用此构造函数以避免重新散列。
- public HashMap(int initialCapacity, float loadFactor): 此 HashMap 构造函数将创建一个具有指定初始容量和负载因子的空 HashMap。在常见情况下,应避免使用此构造函数,因为负载因子0.75在空间和时间成本之间提供了良好的平衡。
- public HashMap(Map<? extends K, ? extends V> m): 创建一个具有与指定映射相同的映射的 Map,并具有0.75的负载因子。
Java HashMap 构造函数示例
下面的代码片段显示了使用上述所有构造函数的 HashMap 示例。
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);
Java HashMap 方法
讓我們來看看 Java 中 HashMap 的重要方法。
- public void clear():此 HashMap 方法將刪除所有映射,並使 HashMap 變為空。
- public boolean containsKey(Object key):如果鍵存在則返回 true,否則返回 false。
- public boolean containsValue(Object value):如果值存在則返回 true,否則返回 false。
- public Set<Map.Entry<K,V>> entrySet():此方法返回 HashMap 映射的 Set 觀點。該集合由地圖支持,因此對地圖的更改會反映在集合中,反之亦然。
- public V get(Object key):返回指定鍵映射的值;如果鍵沒有映射,則返回 null。
- public boolean isEmpty():返回 true,如果沒有鍵值映射存在。
- public Set<K> keySet():返回包含在此地圖中的鍵的 Set 觀點。該集合由地圖支持,因此對地圖的更改會反映在集合中,反之亦然。
- public V put(K key, V value):將指定的值與指定的鍵關聯在此地圖中。如果地圖先前包含鍵的映射,則舊值將被替換。
- public void putAll(Map extends K, ? extends V> m):將指定地圖中的所有映射複製到此地圖。這些映射將取代此地圖對於指定地圖中當前所有鍵的任何映射。
- public V remove(Object key):如果存在,從此地圖中刪除指定鍵的映射。
- public int size():返回此地圖中的鍵值映射數量。
-
public Collection
values() :返回此地圖中包含的值的 Collection 觀點。該集合由地圖支持,因此地圖的更改會反映在集合中,反之亦然。
HashMap 中在 Java 8 中引入了許多新方法。
- public V computeIfAbsent(K key, Function super K, ? extends V> mappingFunction):如果指定鍵尚未與值關聯(或映射為 null),則此方法嘗試使用給定的映射函數計算其值並將其輸入 HashMap,除非為 Null。
- public V computeIfPresent(K key, BiFunction super K, ? super V, ? extends V> remappingFunction):如果指定鍵的值存在且非 null,則嘗試根據鍵及其當前映射的值計算新映射。
- public V compute(K key, BiFunction super K, ? super V, ? extends V> remappingFunction):此 HashMap 方法嘗試為指定鍵及其當前映射的值計算映射。
- public void forEach(BiConsumer<? super K, ? super V> action): 此方法為該映射中的每個項目執行給定的動作。
- public V getOrDefault(Object key, V defaultValue): 與get方法相同,只是如果找不到指定鍵的映射,則返回defaultValue。
- public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction): 如果指定的鍵尚未與值關聯,或者與null關聯,則將其與給定的非null值關聯起來。否則,將與給定重映射函數的結果替換關聯的值,如果結果為null,則刪除它。
- public V putIfAbsent(K key, V value): 如果指定的鍵尚未與值關聯(或映射為null),則將其與給定值關聯並返回null,否則返回當前值。
- public boolean remove(Object key, Object value):僅在目前將指定的鍵映射到指定值時才刪除該鍵的項目。
- public boolean replace(K key, V oldValue, V newValue):僅在目前將指定的鍵映射到指定值時才替換該鍵的項目。
- public V replace(K key, V value):僅在目前將指定的鍵映射到某個值時才替換該鍵的項目。
- public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function):用該條目上的結果替換每個條目的值所執行的結果。
Java HashMap 示例
这是一个用于演示HashMap常用方法的简单Java程序。
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());
}
}
以下是上述Java HashMap示例程序的输出。
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
HashMap在Java中的工作原理是什么?
HashMap 在 Java 中使用它的内部类 Node 推荐阅读: Java 中 hashCode 和 equals 方法的重要性
Java HashMap 加载因子
加载因子用于确定何时对 HashMap 进行重新哈希以及桶的大小将会增加。桶或容量的默认值为 16,加载因子为 0.75。重新哈希的阈值是通过将容量和加载因子相乘来计算的。因此,默认阈值将为 12。因此,当 HashMap 具有超过 12 个映射时,它将被重新哈希,桶的数量将增加到下一个2的幂,即32。请注意,HashMap 的容量始终是2的幂。默认加载因子为 0.75 在空间和时间复杂性之间提供了良好的权衡。但根据您的需求,可以将其设置为不同的值。如果要节省空间,可以将其增加到 0.80 或 0.90,但此时获取/放置操作将花费更多时间。
Java HashMap keySet
Java HashMap 的 keySet 方法返回 HashMap 中键的 Set 视图。此 Set 视图由 HashMap 支持,HashMap 中的任何更改都会反映在 Set 中,反之亦然。下面是一个演示 HashMap keySet 示例的简单程序,以及如果需要一个不由映射支持的 keySet 应该如何处理。
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
}
}
上述程序的输出将清楚地表明 keySet 受映射支持。
[1, 2, 3]
[1, 2, 3, 4]
{2=2, 3=3, 4=4}
[2, 3, 4]
Java HashMap 值
Java HashMap 值方法返回映射中值的集合视图。此集合由 HashMap 支持,因此 HashMap 中的任何更改都将反映在值集合中,反之亦然。下面的简单示例确认了 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
}
}
上述程序的输出如下。
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}
Java HashMap entrySet
Java HashMap entrySet 方法返回映射的 Set 视图。此 entrySet 由 HashMap 支持,因此地图中的任何更改都会反映在条目集中,反之亦然。请查看下面的 HashMap entrySet 示例程序。
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);
}
}
上述程序产生的输出如下。
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]
Java HashMap putIfAbsent
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);
}
}
上述程序的输出为;
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
Java HashMap forEach
HashMap forEach方法是在Java 8中引入的。这是一种非常有用的方法,可以为地图中的每个条目执行给定的操作,直到所有条目都被处理完或操作引发异常为止。
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);
// 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);
}
}
上述HashMap forEach示例程序的输出是:
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
HashMap replaceAll方法可用于用调用该条目上给定函数的结果替换每个条目的值。此方法在Java 8中添加,我们可以使用lambda表达式作为此方法的参数。
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);
// 使用lambda表达式的replaceAll
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;
}
}
上述HashMap replaceAll程序的输出是:
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
HashMap computeIfAbsent方法仅在地图中不存在键时才计算值。计算值后,如果值不为null,则将其放入地图中。
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
// 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;
}
}
上述程序的输出是;
{null=100, 1=10, 2=20, 3=3, 4=4}
Java HashMap computeIfPresent
Java HashMap computeIfPresent 方法如果指定的鍵存在且值不為 null,則重新計算該值。如果函數返回 null,則映射將被刪除。
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;
}
}
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
如果要基於鍵和值對所有映射應用函數,則應該使用 compute 方法。如果沒有映射且使用了此方法,則對於 compute 函數,值將為 null。
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);
}
}
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
如果指定的鍵不存在或與 null 關聯,則將其與給定的非 null 值關聯。否則,將關聯值替換為給定重新映射函數的結果,如果結果為 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();
// 如果鍵或值為 null,merge 會拋出 NullPointerException
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);
}
}
上述程序的輸出是;
{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}
那就是 Java 中的 HashMap 全部了,希望没有漏掉什么重要的内容。如果喜欢的话,也请与他人分享。参考:API 文档
Source:
https://www.digitalocean.com/community/tutorials/java-hashmap