一、Map介面繼承樹

Map:雙列資料,存盤key-value對的資料 ---類似于高中的函式:y = f(x)
A.HashMap:作為Map的主要實作類;執行緒不安全的,效率高;存盤null的key和value
a.LinkedHashMap:保證在遍歷map元素時,可以按照添加的順序實作遍歷,
原因:在原有的HashMap底層結構基礎上,添加了一對指標,指向前一個和后一個元素,
對于頻繁的遍歷操作,此類執行效率高于HashMap.
B.TreeMap:保證按照添加的key-value對進行排序,實作排序遍歷,此時考慮key的自然排序或定制排序
底層使用紅黑樹
C.Hashtable:作為古老的實作類:執行緒安全的,效率低;不能存盤null的key和value
c.Properties:常用來處理組態檔,key和value都是String型別

HashMap的底層:陣列 + 鏈表(JDK及之前)
陣列 + 鏈表 + 紅黑樹(jdk 8)
Map結構的理解:
> Map中的key:無序的、不可重復的,使用Set存盤所有的key --->key所在的類要重寫equals()和hashCode()(以HashMap為例)
> Map中的value:無序的、可重復的,使用Collection存盤所有的value --->value所在的型別要重寫equals()
> 一個鍵值對:key-value構成了一個Entry物件,
> Map中的entry:無序的、不可重復的,使用Set存盤所有的entry
二、Map介面中的常用方法

/*
添加、洗掉、修改操作:
Object put(Object key,Object value): 將指定key-value添加到(或修改)當前map物件中
void putAll(Map m):將m中的所有key-value對存放到當前map中
Object remove(Object key):移除指定key的key-value對,并回傳value
void clear():清空當前map中的所有資料
*/
public void test1(){
HashMap map = new HashMap();
//添加
map.put("AA",123);
map.put(45,123);
map.put("BB",56);
//修改
map.put("AA",87);
System.out.println(map);//{AA=87, BB=56, 45=123}
HashMap map1 = new HashMap();
map.put("CC",123);
map.put("DD",123);
map.putAll(map1);
System.out.println(map);//{AA=87, BB=56, CC=123, DD=123, 45=123}
//remove(Object key)
Object value = map.remove("CC");
System.out.println(value);//123
System.out.println(map);//{AA=87, BB=56, DD=123, 45=123}
//clear()
map.clear();//與map = null操作不同;map物件還在,只是里面的資料沒了
System.out.println(map.size());//0
System.out.println(map);//{}
}
/*
元素查詢的操作:
Object get(Object key):獲取指定key對應的value
boolean containsKey(Object key):是否包含指定的value
int size():回傳map中key-value對的個數
boolean isEmpty():判斷當前map是否為空
boolean equals(Object obj):判斷當前map和引數物件obj是否相等
*/
public void test2(){
HashMap map = new HashMap();
map.put("AA",123);
map.put(45,123);
map.put("BB",56);
//Object get(Object key)
System.out.println(map.get(45));//123
//containsKey(Object key)
boolean isExit = map.containsKey("BB");
System.out.println(isExit);//true
isExit = map.containsValue(123);
System.out.println(isExit);//true
map.clear();
System.out.println(map.isEmpty());//true
}
/*
元視圖操作的方法:
Set keySet():回傳所有key構成的Set集合
Collection values(): 回傳所有value構成的Collection集合
Set entrySet():回傳所有key-value對構成的Set集合
*/
public void test3(){
HashMap map = new HashMap();
map.put("AA",123);
map.put(45,123);
map.put("BB",56);
//遍歷所有的key集:keySet()
Set set = map.keySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());//AA BB 45
}
System.out.println();
//遍歷所有的value集:value()
Collection values = map.values();
for(Object obj : values){
System.out.println(obj);// 123 56 123
}
System.out.println();
//遍歷所有的key-value
//方式一:entrySet()
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
while(iterator1.hasNext()){
Object obj = iterator1.next();
//entrySet集合中的元素都是entry
Map.Entry entry = (Map.Entry)obj;
System.out.println(entry.getKey() + "---->" + entry.getValue());
//AA---->123
//BB---->56
//45---->123
}
System.out.println();
//方式二:
Set keySet = map.keySet();
Iterator iterator2 = keySet.iterator();
while(iterator2.hasNext()){
Object key = iterator2.next();
Object value = map.get(key);
System.out.println(key + "======" + value);
//AA======123
//BB======56
//45======123
}
}
總結:常用方法
添加:put(Object key,Object value)
洗掉:remove(Object key)
修改:put(Object key,Object value)
查詢:get(Object key)
長度:size()
遍歷:keySet() / values() / entrySet()
三、原始碼分析
1. HashMap的底層實作原理?以jdk7為例說明:

HashMap map = new HashMap():
在實體化以后,底層創建了長度是16的一維陣列Entry[] table.
...可能已經執行過多次put...
map.put(key1,value1):
首先,呼叫key1所在類的hashCode()計算key1哈希值,此哈希值經過某種演算法計算以后,得到在Entry陣列中的存放位置,
如果此位置上的資料為空,此時的key1-value1添加成功,----情況1
如果此位置上的資料不為空,(意味著此位置上存在一個或多個資料(以鏈表形式存在)),比較key1和已經存在的一個或多個資料
的哈希值:
如果key1的哈希值與已經存在的資料的哈希值都不相同,此時key-value1添加成功,----情況2
如果key1的哈希值和已經存在的某一個資料(key-value2)的哈希值相同,繼續比較:呼叫key1所在類的equals(key2)方法
如果equals()回傳false:此時key1-value1添加成功,----情況3
如果equals()回傳true:使用value1替換value2.(修改作用的體現)
補充:關于情況2和情況3:此時key1-value1和原來的資料以鏈表的方式存盤,
在不斷地添加程序中,會涉及到擴容問題,當超出臨界值(且要存放的位置非空)時,擴容,默認的擴容方式:擴容為原來容量的兩倍,并將原有的資料復制過來,
jdk8相較于jdk7在底層實作方面的不同:
1.new HashMap():底層沒有創建一個長度為16的陣列
2.jdk 8底層的陣列是:Node[],而非Entry[]
3.首次呼叫put()方法時,底層創建長度為16的陣列
4.jdk7底層結構只有:陣列 + 鏈表,jdk8中底層結構:陣列 + 鏈表 + 紅黑樹,
5. 形成鏈表時,七上八下(jdk7:新的元素指向舊的元素;jdk8:舊的元素指向新的元素)
當陣列的某一個索引位置上的元素以鏈表形式存在的資料個數 > 8 且當前陣列的長度 > 64時,
此時此索引位置上的所有資料改為使用紅黑樹存盤,(重要優化:提高查找效率)
DEFAULT_INITIAL_CAPACITY:HashMap的默認容量,16
DEFAULT_LOAD_FACTOR:HashMap的默認加載因子:0.75
threshold:擴容的臨界值,=容量*填充因子:16*0.75 =>12
TREEIFY_THRESHOLD:Bucket中鏈表長度大于該默認值,轉化為紅黑樹:8
MIN_TREEIFY_CAPACITY:桶中的Node被樹化時最小的hash表容量:64

2.LinkedHashMap的底層實作原理(了解)
LinkedHashMap的底層使用的結構與HashMap相同,因為LinkedHashMap繼承于HashMap,區別就在于:LinkedHashMap內部提供了Entry,替換HashMap中的Node.

原始碼中:
static class Entry<K,V> extends HashMap.Node<K,V>{
Entry<K,V> before,after;//能夠記錄添加的元素的先后順序
Entry(int hash, K key, V value,Node<K,V> next){
super(hash, key, value, next);
}
}
TreeMap
//向TreeMap中添加key-value,要求key必須是有同一個類創建的物件
//因為要按照key進行排序:自然排序、定制排序
@Test
public void test1() {
TreeMap map = new TreeMap();
User u1 = new User("Tom", 23);
User u2 = new User("Jerry", 32);
User u3 = new User("Jack", 20);
User u4 = new User("Rose", 18);
map.put(u1, 98);
map.put(u2, 89);
map.put(u3, 76);
map.put(u4, 100);
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
while (iterator1.hasNext()) {
Object obj = iterator1.next();
//entrySet集合中的元素都是entry
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey() + "---->" + entry.getValue());
//User{name='Jack', age=20}---->76
//User{name='Jerry', age=32}---->89
//User{name='Rose', age=18}---->100
//User{name='Tom', age=23}---->98
}
}
Hashtable
> Hashtable 是個古老的 Map 實作類,JDK1.0就提供了,不同于 HashMap , Hashtable 是執行緒安 全的,
> Hashtable 實作原理和 HashMap 相同,功能相同,底層都使用哈希表結構,查詢速度快,很多情況 下可以互用,
> 與 HashMap 不同, Hashtable 不允許使用 null 作為 key 和 value
> 與 HashMap -樣, Hashtable 也不能保證其中 Key - Value 對的順序
> Hashtable 判斷兩個 key 相等、兩個 value 相等的標準,與 HashMap 一致,
四、Collections工具類
常用方法及其測驗


public void test1(){
ArrayList list = new ArrayList();
list.add(123);
list.add(43);
list.add(765);
list.add(765);
list.add(765);
list.add(-97);
list.add(0);
System.out.println(list);
// Collections.reverse(list);
// Collections.shuffle(list);
// Collections.swap(list,1,2);
int frequency = Collections.frequency(list, 765);
System.out.println(list);
System.out.println(frequency);
/*
Collections類中提供了多個synchronizedXxx()方法,該方法可使將指定集合包裝成執行緒同步的集合,從而可以解決多執行緒
并發訪問集合時的執行緒安全問題
*/
List list1 = Collections.synchronizedList(list);
}
public void test2(){
List list = new ArrayList();
list.add(123);
list.add(43);
list.add(765);
list.add(-97);
list.add(0);
//報例外:IndexOutOfBoundsException
// List dest = new ArrayList();
// Collections.copy(dest,list);
//正確的:
List dest = Arrays.asList(new Object[list.size()]);
System.out.println(dest.size());
Collections.copy(dest,list);//5
System.out.println(dest);//[123, 43, 765, -97, 0]
}


轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/295168.html
標籤:其他
上一篇:【Webpack 開發環境配置】萬字長文總結學習如何打包樣式資源、html資源、圖片資源和其他資源?devServer是什么,如何配置?
