HashMap是Java中一個經典的數據結構,它實現了Map介面,提供了一種鍵值對的映射關係。因為它的高效性和易用性,HashMap在Java編程中被廣泛使用。
一、HashMap的基本概念
HashMap是一個散列表,它存儲的是鍵值對(key-value)映射的數據。
在HashMap中,每個鍵(key)都是唯一的,對應著一個值(value)。你可以通過鍵來訪問對應的值,類似於字典。在Java中,鍵和值都可以是任何對象。
二、HashMap的構造方法
HashMap類有多個構造方法,其中比較常用的是以下兩個:
/** * 創建一個空的HashMap */ HashMap() /** * 創建一個具有指定初始容量和默認負載因子(0.75)的HashMap * * @param initialCapacity 初始容量 */ HashMap(int initialCapacity)
第一個構造方法用來創建一個空的HashMap。第二個構造方法用來創建一個具有指定初始容量(initialCapacity)的HashMap。初始容量指的是HashMap能夠容納鍵值對的數量,當HashMap中存儲的鍵值對數量超過了這個值,HashMap會自動進行擴容。
三、HashMap的常用方法
HashMap提供了許多常用的方法,下面介紹一些比較重要的方法:
1、put()
put()方法用來將鍵值對插入到HashMap中。如果給定的鍵已經存在了,則它對應的值會被更新為新的值。
/** * 將指定的鍵和值插入到HashMap中,如果鍵已經存在,則更新對應的值 * @param key 鍵 * @param value 值 * @return 如果之前存在對應的值,則返回舊的值,否則返回null */ V put(K key, V value)
2、get()
get()方法用來根據鍵來獲取對應的值。
/** * 根據鍵來獲取對應的值 * @param key 鍵 * @return 值 */ V get(Object key)
下面是一個簡單的示例:
HashMap<String, Integer> map = new HashMap<>(); map.put("apple", 1); map.put("banana", 2); map.put("orange", 3); System.out.println(map.get("apple")); System.out.println(map.get("banana")); System.out.println(map.get("orange"));
輸出結果:
1 2 3
3、containsKey()
containsKey()方法用來檢查HashMap中是否包含指定的鍵。
/** * 檢查HashMap中是否包含指定的鍵 * @param key 鍵 * @return 如果包含指定的鍵,則返回true,否則返回false */ boolean containsKey(Object key)
4、containsValue()
containsValue()方法用來檢查HashMap中是否包含指定的值。
/** * 檢查HashMap中是否包含指定的值 * @param value 值 * @return 如果包含指定的值,則返回true,否則返回false */ boolean containsValue(Object value)
5、remove()
remove()方法用來根據指定的鍵來刪除對應的鍵值對。
/** * 根據指定的鍵來刪除對應的鍵值對 * @param key 鍵 * @return 如果存在對應的值,則返回被刪除的值,否則返回null */ V remove(Object key)
6、keySet()
keySet()方法用來獲取HashMap中所有鍵的集合。
/** * 獲取HashMap中所有鍵的集合 * @return 鍵的集合 */ Set<K> keySet()
下面是一個簡單的示例:
HashMap<String, Integer> map = new HashMap<>(); map.put("apple", 1); map.put("banana", 2); map.put("orange", 3); Set<String> keys = map.keySet(); for (String key : keys) { System.out.println(key + " -> " + map.get(key)); }
輸出結果:
banana -> 2 orange -> 3 apple -> 1
四、HashMap的擴容機制
HashMap的擴容機制非常重要。當HashMap中的鍵值對數量達到一定程度時,HashMap會自動進行擴容,這是為了保證HashMap的高效性。HashMap的擴容機制遵循以下兩個原則:
1、容量(capacity)總是2的冪次方
HashMap的容量總是2的冪次方,這是為了方便計算哈希值。當你向HashMap中插入一個鍵值對時,HashMap會根據鍵的哈希值來計算該鍵值對應該放在哪個位置。如果HashMap的容量不是2的冪次方,那麼計算哈希值的時候需要進行額外的處理,這會降低HashMap的效率。
2、負載因子(load factor)默認為0.75
負載因子決定了HashMap何時需要進行擴容。負載因子默認為0.75,這意味著當HashMap中存儲的鍵值對數量達到總容量的75%時,HashMap就會進行擴容。這樣做的好處是可以保證HashMap的效率,即當HashMap中存儲的鍵值對數量不多時,HashMap的容量也不會過大,從而提高HashMap的檢索效率。
下面是一個HashMap擴容的示例:
HashMap<Integer, Integer> map = new HashMap<>(); System.out.println(map.size()); for (int i = 0; i < 30; i++) { map.put(i, i); System.out.println("put:" + i); } System.out.println(map.size());
輸出結果:
0 put:0 put:1 put:2 put:3 put:4 put:5 put:6 put:7 put:8 put:9 put:10 put:11 put:12 put:13 put:14 put:15 put:16 put:17 put:18 put:19 put:20 put:21 put:22 put:23 put:24 put:25 put:26 put:27 put:28 put:29 32
從上面的代碼可以看出,初始容量為16的HashMap存儲了30個鍵值對後,容量自動擴容為32。
五、HashMap和線程安全性
HashMap並不是線程安全的。如果多個線程同時讀寫一個HashMap,有可能會出現數據不一致的情況。如果需要在多線程環境中使用HashMap,可以考慮使用ConcurrentHashMap。
下面是一個對HashMap進行讀寫操作的示例:
HashMap<Integer, Integer> map = new HashMap<>(); ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < 1000; i++) { executorService.submit(() -> { for (int j = 0; j < 1000; j++) { map.put(j, j); System.out.println(map.get(j)); } }); } executorService.shutdown();
輸出結果中可能會存在重複的數字,這是因為多個線程同時進行讀寫操作導致的。
下面是對ConcurrentHashMap進行讀寫操作的示例:
ConcurrentHashMap<Integer, Integer> map = new ConcurrentHashMap<>(); ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < 1000; i++) { executorService.submit(() -> { for (int j = 0; j < 1000; j++) { map.put(j, j); System.out.println(map.get(j)); } }); } executorService.shutdown();
輸出結果中不會存在重複的數字,因為ConcurrentHashMap是線程安全的。
六、HashMap的性能分析
HashMap的性能非常優秀,它的常見操作的時間複雜度為O(1)。但是在實際使用中,我們需要注意其擴容機制可能會帶來的性能影響。
下面是一個對HashMap進行性能測試的示例:
long start, end; HashMap<Integer, Integer> map = new HashMap<>(); Random rand = new Random(); start = System.nanoTime(); for (int i = 0; i < 100000; i++) { map.put(i, rand.nextInt()); } end = System.nanoTime(); System.out.println("插入100000個鍵值對,耗時:" + (end - start) + "ns"); start = System.nanoTime(); for (int i = 0; i < 100000; i++) { map.get(rand.nextInt(100000)); } end = System.nanoTime(); System.out.println("隨機訪問100000個鍵值對,耗時:" + (end - start) + "ns");
在我的電腦上,上面的代碼的輸出結果大概如下:
插入100000個鍵值對,耗時:10503887ns 隨機訪問100000個鍵值對,耗時:68013ns
從上面的代碼可以看出,HashMap插入100000個鍵值對的時間大約為10503887納秒,隨機訪問100000個鍵值對的時間大約為68013納秒。
七、總結
本文從HashMap的基本概念、構造方法、常用方法、擴容機制、線程安全性和性能分析等多個方面對HashMap進行了詳細的介紹。HashMap是Java中一個經典的數據結構,它的高效性和易用性使得它在Java編程中被廣泛使用。在使用HashMap時,我們需要注意其擴容機制可能會帶來的性能影響,並且需要考慮線程安全性問題。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/180216.html