複習12:Map

集合:Map

Map是什麼

特殊的集合接口

Map的特點

  • Map中的元素被稱爲鍵值對(key-value),一個key對應一個value,例如電話簿中每一個名字對應一個電話號碼(key不可重複,value可重複)

    • key:Set組成
    • value:List組成
  • 底層實現是散列表,即數組+鏈表(List+Set)+[樹]

Map的實現類和子接口

  • HashMap:線程不安全,無序,key不可重複(不可重複的原理與HashSet相同)
    • Hashtable:線程安全,類似StringBuffer、Vector
  • SortedMap
    • TreeMap:可排序(原理與TreeSet相同),對key進行排序
  • LinkedHashMap

TreeMap排序時,若選擇傳入構造器Comparator,則構造器的泛型類型必須與key的泛型類型一致

Map的迭代

  • Map不能使用迭代器(沒有實現Iterable接口)
  • **!!!**遍歷key:Set keySet()
package demo;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Demo1<E> {
	public static void main(String[] args) {
		Map<String,String> map = new HashMap<String,String>();
		map.put("1","a");
		map.put("2","b");
		map.put("3","c");
		map.put("4","d");
		//返回key的set集合
		Set<String> keys = map.keySet();
		for(String set : keys) {
			System.out.println(set+"="+map.get(set));
		}
	}
}
  • 遍歷value:Collection values()
package demo;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Demo1<E> {
	public static void main(String[] args) {
		Map<String,String> map = new HashMap<String,String>();
		map.put("1","a");
		map.put("2","b");
		map.put("3","c");
		map.put("4","d");
		
		Collection<String> values = map.values();
		for(String str : values) {
			System.out.println(str);
		}
	}
}
 

無法通過value得到key,所以這種方式的遍歷只能獲得Map的value

  • **!!!**遍歷key-value:Set< Entry<k,v> > entrySet()
package demo;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class Demo1<E> {
	public static void main(String[] args) {
		Map<String,String> map = new HashMap<String,String>();
		map.put("1","a");
		map.put("2","b");
		map.put("3","c");
		map.put("4","d");
		
		Set<Entry<String,String>> entrys = map.entrySet();
		for(Entry<String,String> entry : entrys) {
			System.out.println(entry);
		}
		
	}
}
 

Entry<k,v>是Map的一個內部類

Map的API

  • put(key,value):添加新的鍵值對(hashCode()根據key計算位置)
    • 可能會出現的兩種情況:
      • key不同,hashCode()的計算結果相同:鏈表方式存儲,原來的元素向後推
      • key值相同:新的鍵值對覆蓋原來的鍵值對

注意1(重要):key和value可以爲null

注意2:HashSet:新元素不會替換舊元素

  • get(key):通過key獲得對應的value
  • remove(key):通過key刪除整個鍵值對,返回被刪除的value
    • 如果查詢不到鍵值對,返回null
  • contains…(…):
    • containsKey(key)
    • containsValue(value)
  • isEmpty()
  • putAll(Map map)

HashMap

HashMap的底層實現

數組+單向鏈表+紅黑樹

  • 當鏈表中的數量超過8個時,鏈表轉換爲紅黑樹,所以如果哈希衝突(不同元素的hashCode()得到相同的結果)多的話,數組的元素將會是紅黑樹
HashMap的長度與擴容方式
  • HashMap的初始長度:16
  • HashMap的加載因子:0.75
    • 加載因子:當Map中的鍵值對數量達到長度的0.75時,Map自動擴容(Hashtable相同)
  • HashMap的擴容方式:old*2
    • 例如,Map的初始長度爲16,當Map中的鍵值對個數達到12個時,Map的長度自動擴容爲32

選擇加載因子爲0.75的原因:

  • HashMap擴容時,鍵值對的位置都要進行重新計算
  • 新插入的鍵值對的位置也要進行計算,當剩餘空間太小時,計算所需時間會變長

綜合考慮以上兩點,當加載因子爲0.75時,效率比較高

  • Hashtable的初始長度:11

  • Hashtable的加載因子:0.75

  • Hashtable的擴容方式:old*2+1

Set的初始長度、加載因子、擴容方式都與Map相同

LinkedHashMap

LinkedHashMap是什麼

LinkedList與HashMap的結合,底層仍然是HashMap

LinkedHashMap的實現

雙向鏈表+散列表

LinkedHashMap的特點
  • key不可重複
  • 有序(有序指元素的順序與添加的先後順序一致,並非是排序)
  • 節點多了before和after屬性(對應LinkedList的provious和next)

Map實現類的對比

HashMap Hashtable LinkedHashMap TreeMap
元素排序 無序 無序 加入順序 字典順序
元素 key和value可以爲null 不接受null key和value可以爲null 僅接受value爲null
底層實現 數組+單向鏈表+紅黑樹 數組+鏈表 散列表+雙向鏈表 紅黑樹
線程安全 非synchronized synchronized 非synchronized 非synchronized
線程同步 不同步 同步 不同步 不同步
效率
默認長度 16 11
加載因子 0.75 0.75
擴容方式 old*2 old*2+1

擴展:ConcurrentHashMap

出現原因

HashMap線程不安全,Hashtable性能不好

底層實現

由16個segment組成,每個sgment都是線程安全的HashMap(鎖分段技術)

  • 將數據分成一段一段(segment)地存儲,然後給每個數據段配一把鎖,當一個線程佔用鎖訪問其中某個數據段時,其它數據段的數據也能被其它線程訪問

上述實現是基於JDK1.7,在JDK1.8中ConcurrentHashMap完全是在HashMap的基礎上進行加鎖

特點
  • 對segment的改寫操作,不影響其它segment的使用
    • 理想情況下,最多支持6個線程併發修改segment,這16個線程分別訪問不同的segment
  • segment加鎖時,所以讀線程是不會受到阻塞的
使用場景

高併發

方法的實現原理
  • get():不加鎖,先定位到segment,然後再找到頭結點進行讀取操作。因爲value是volatile變量,所以可以保證在競爭條件時讀取到最新的值,如果讀到的value是null,則數據可能正在被修改,那麼就調用方法ReadValueUnderLock(),加鎖保證讀到的數據是正確的。
  • Put():加鎖,一律添加到hash鏈的頭部
  • Remove():加鎖,由於next是final類型不可改變,所以必須把刪除的節點之前的節點都複製一遍。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章