HashMap 是一個(gè)散列表,它存儲(chǔ)的內(nèi)容是鍵值對(duì)(key-value)映射。
HashMap 繼承于AbstractMap,實(shí)現(xiàn)了Map、Cloneable、java.io.Serializable接口。
HashMap 的實(shí)現(xiàn)不是同步的,這意味著它不是線程安全的。它的key、value都可以為null。此外,HashMap中的映射不是有序的。
HashMap 的實(shí)例有兩個(gè)參數(shù)影響其性能:“初始容量” 和 “加載因子”。容量 是哈希表中桶的數(shù)量,初始容量 只是哈希表在創(chuàng)建時(shí)的容量。加載因子 是哈希表在其容量自動(dòng)增加之前可以達(dá)到多滿的一種尺度。當(dāng)哈希表中的條目數(shù)超出了加載因子與當(dāng)前容量的乘積時(shí),則要對(duì)該哈希表進(jìn)行 rehash 操作(即重建內(nèi)部數(shù)據(jù)結(jié)構(gòu)),從而哈希表將具有大約兩倍的桶數(shù)。
通常,默認(rèn)加載因子是 0.75, 這是在時(shí)間和空間成本上尋求一種折衷。加載因子過(guò)高雖然減少了空間開(kāi)銷,但同時(shí)也增加了查詢成本(在大多數(shù) HashMap 類的操作中,包括 get 和 put 操作,都反映了這一點(diǎn))。在設(shè)置初始容量時(shí)應(yīng)該考慮到映射中所需的條目數(shù)及其加載因子,以便最大限度地減少 rehash 操作次數(shù)。如果初始容量大于最大條目數(shù)除以加載因子,則不會(huì)發(fā)生 rehash 操作。
構(gòu)造方法
| 方法 | 描述 |
| HashMap() | 默認(rèn)構(gòu)造方法 |
| HashMap(int capacity) | 構(gòu)造一個(gè)指定初始容量和默認(rèn)加載因子(0.75)的空HashMap |
| HashMap(int capacity, float loadFactor) | 構(gòu)造一個(gè)指定容量到小和加載因子的空HashMap |
| HashMap(Map<? extends K, ? extends V> map) | 構(gòu)造一個(gè)映射關(guān)系與指定Map相同的新HashMap |
| 常用方法 | 描述 |
| put(K key, V value) | 通過(guò)此方法將鍵值對(duì)添加到HashMap中 |
| putAll(Map<? extends K, ? extends V> map) | 將另一個(gè)map集合復(fù)制到此集合中 |
import java.util.HashMap;
import java.util.Set;
import java.util.Iterator;
import java.util.Collection;
import java.util.Map.Entry;
public class HashMapExercise {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<>();
// 添加方法
map.put("小紅", 18);
map.put("小明", 30);
map.put("小輝", 20);
map.put("小李", 18);
System.out.println(map); // {小輝=18, 小明=18, 小李=18, 小紅=18}
//判斷鍵是否存在
boolean flg = map.containsKey("小明");
System.out.println(flg); // true
// 判斷值是否存在
flg = map.containsValue(18);
System.out.println(flg); // true
// 判斷集合是否為空
flg = map.isEmpty();
System.out.println(flg); // false
// 根據(jù)鍵獲取值
Integer i = map.get("小李");
System.out.println(i); // 18
// 獲取集合中元素的個(gè)數(shù)
int size = map.size();
System.out.println(size); // 4
// Map集合的四種遍歷方式
// 遍歷結(jié)果
// Key:小輝,Value:20
// Key:小明,Value:30
// Key:小李,Value:18
// Key:小紅,Value:18
// 第一種: 通過(guò)keySet()方法獲取到所有鍵的Set集合
Set<String> keySet = map.keySet(); // map集合使用keySet()方法返回的是所有的key的值
for (String str : keySet) {
Integer in = map.get(str);
System.out.println("Key:"+str+",Value:"+in);
}
// 第二種: 通過(guò)entrySet()方法獲取所有的鍵值對(duì)的Set集合 Entry<String, Integer>
Set<HashMap.Entry<String, Integer>> entrySet = map.entrySet(); // map集合使用entrySet()方法返回的是所有的鍵值對(duì)
// 通過(guò)這種方式獲得的所有鍵值對(duì)還需要使用迭代器進(jìn)行遍歷
Iterator<Map.Entry<String, Integer>> iterator = entrySet.iterator();
while (iterator.hasNext()) {
HashMap.Entry<String, Integer> entry = iterator.next();
System.out.println("Key:"+entry.getKey()+",Value:"+entry.getValue());
}
// 第三種: 使用foreach循環(huán)進(jìn)行遍歷;當(dāng)容量較大時(shí),推薦.
for (HashMap.Entry<String , Integer> entry: map.entrySet()) {
// Map.Entry<String, Integer> 映射項(xiàng)(鍵-值對(duì))
// 這種方法也可用于 keySet()和values()
System.out.println("Key:"+entry.getKey()+",Value:"+entry.getValue());
}
// 第四種: 通過(guò)values()方法獲取所有值的Collection集合
// 遍歷結(jié)果
// value:20
// value:30
// value:18
// value:18
Collection<Integer> values = map.values();
Iterator<Integer> it = values.iterator();
while(it.hasNext()) {
Integer value = it.next();
System.out.println("value:"+value); // 我們不能通過(guò)值去進(jìn)行鍵的查找
}
}
}
HashMap集合就是Map接口實(shí)現(xiàn)的類,Map能實(shí)現(xiàn)的功能,HashMap基本都能實(shí)現(xiàn).
HashMap和Hashtable的區(qū)別
它們的主要的區(qū)別有:線程安全性,同步(synchronization),以及速度。
- HashMap幾乎可以等價(jià)于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受為null的鍵值(key)和值(value),而Hashtable則不行)。
- HashMap是非synchronized,而Hashtable是synchronized,這意味著Hashtable是線程安全的,多個(gè)線程可以共享一個(gè)Hashtable;而如果沒(méi)有正確的同步的話,多個(gè)線程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的擴(kuò)展性更好。
- 另一個(gè)區(qū)別是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以當(dāng)有其它線程改變了HashMap的結(jié)構(gòu)(增加或者移除元素),將會(huì)拋出ConcurrentModificationException,但迭代器本身的remove()方法移除元素則不會(huì)拋出ConcurrentModificationException異常。但這并不是一個(gè)一定發(fā)生的行為,要看JVM。這條同樣也是Enumeration和Iterator的區(qū)別。
- 由于Hashtable是線程安全的也是synchronized,所以在單線程環(huán)境下它比HashMap要慢。如果你不需要同步,只需要單一線程,那么使用HashMap性能要好過(guò)Hashtable。HashMap不能保證隨著時(shí)間的推移Map中的元素次序是不變的。
要注意的一些重要術(shù)語(yǔ):
sychronized意味著在一次僅有一個(gè)線程能夠更改Hashtable。就是說(shuō)任何線程要更新Hashtable時(shí)要首先獲得同步鎖,其它線程要等到同步鎖被釋放之后才能再次獲得同步鎖更新Hashtable。
Fail-safe和iterator迭代器相關(guān)。如果某個(gè)集合對(duì)象創(chuàng)建了Iterator或者ListIterator,然后其它的線程試圖“結(jié)構(gòu)上”更改集合對(duì)象,將會(huì)拋出ConcurrentModificationException異常。但其它線程可以通過(guò)set()方法更改集合對(duì)象是允許的,因?yàn)檫@并沒(méi)有從“結(jié)構(gòu)上”更改集合。但是假如已經(jīng)從結(jié)構(gòu)上進(jìn)行了更改,再調(diào)用set()方法,將會(huì)拋出IllegalArgumentException異常。
結(jié)構(gòu)上的更改指的是刪除或者插入一個(gè)元素,這樣會(huì)影響到map的結(jié)構(gòu)。
我們能否讓HashMap同步?
HashMap可以通過(guò)下面的語(yǔ)句進(jìn)行同步:
Map m = Collections.synchronizeMap(hashMap);
結(jié)論
Hashtable和HashMap有幾個(gè)主要的不同:線程安全以及速度。僅在你需要完全的線程安全的時(shí)候使用Hashtable,而如果你使用Java 5或以上的話,請(qǐng)使用ConcurrentHashMap吧。