一: Map: 雙列數據,存儲 key-value 對的數據 ---類似於高中的函數: y = f(×)
-
HashMap: 作為 Map 的主要實現類; 線程不安全的,效率高; 存儲null的key和value
1.1 LinkedHashMap: 保證在遍歷map元素時,可以按照添加的順序實現遍歷。
原因:在原有的HashMap底層結構基礎上,添加了一對指針,指向前一個和后一個元素。對於頻繁的遍歷操作,此類執行效率高於HashMap。 -
TreeNMap: 保證按照添加的 key-value 對進行排序, 實現排序遍歷。此時考慮key的自然排序或定制排序。底層采用紅黑樹的存儲結構,有序,查詢速度比 List 快。
-
Hashtable:作為古老的實現類; 線程安全的,效率低;不能存儲null的key和value
3.1 Properties: 常用來處理配置文件。key和value都是String類型
HashMap的底層: 數組+鏈表 (jdk7及之前)
數組+鏈表+紅黑樹 (jdk 8)
二:Map結構的理解
Map中的 Key: 無序的、不可重復的,使用 Set 存儲所有的 key ---> key 所在的類要重寫 equals() 和 hashCode() (以 HashMap為例)
Map中的 value: 無序的、可重復的,使用 Collection 存儲所有的 value ---> value 所在的類要重寫 equals()
一個鍵值對: key-value 構成了一個 Entry 對象。
Map 中的 entry: 無序的、不可重復的,使用 Set 存儲所有的 entry
三: HashMap的底層實現原理 以jdk7為例說明:
HashMap map = new HashMap():
在實例化以后,底層創建了長度是16的一維數組Entry[] table
....可能已經執行過多次put...
map.put( key1 , value1):
首先,調用 key1 所在類的 hashCode() 計算 key1 哈希值,此哈希值經過某種算法計算以后,得到在 Entry 數組中的存放位置。
- 如果此位置上的數據為空,此時的 key1-value1 添加成功。----情況1
- 如果此位置上的數據不為空,(意味着此位置上存在一個或多個數據(以鏈表形式存在)),比較 key1 和已經存在的一個或多個數據的哈希值:
2.1 如果 key1 的哈希值與已經存在的數據的哈希值都不相同,此時 key1-value1 添加成功。----情況2
2.2 如果 key1 的哈希值和已經存在的某一個數據(key2-value2)的哈希值相同,繼續比較: 調用 key1 所在類的 equals(key2)
2.2.1 如果 equals() 返回 false: 此時key1-value1添加成功。----情況3
2.2.2 如果 equals() 返回 true: 使用 value1 替換 value2 。
補充: 關於情況2和情況3:此時 key1-value1 和原來的數據以鏈表的方式存儲。
在不斷的添加過程中,會涉及到擴容問題,當超出臨界值(且要存放的位置非空)時,擴容。默認的擴容方式: 擴容為原來容量的2倍,並將原有的數據復制過來。
jdk8 相較於 jdk7 在底層實現方面的不同:
- new HashMap(): 底層沒有創建一個長度為16的數組
- jdk8底層的數組是: Node[],而非 Entry[]
- 首次調用put()方法時,底層創建長度16的數組
- 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
package com.klvchen.java;
import org.junit.Test;
import java.util.*;
public class MapTest {
@Test
public void test5(){
Map 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());
}
System.out.println("--------------------");
//遍歷所有的 value集:values()
Collection values = map.values();
for (Object obj : values) {
System.out.println(obj);
}
System.out.println("--------------------");
//遍歷所有的 key-value
//方式一: entrySet()
//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());
}
//方式二
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);
}
}
@Test
public void test4(){
Map 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));
//conainsKey(Oject key)
boolean isExist = map.containsKey("BB");
System.out.println(isExist);
isExist = map.containsKey(123);
System.out.println(isExist);
map.clear();
System.out.println(map.isEmpty());
}
@Test
public void test3(){
Map map = new HashMap();
//添加
map.put("AA", 123);
map.put(45, 123);
map.put("BB", 56);
//修改
map.put("AA", 87);
System.out.println(map);
Map map1 = new HashMap();
map1.put("CC", 123);
map1.put("DD", 123);
map.putAll(map1);
System.out.println(map);
//remove(Object key)
Object value = map.remove("CC");
System.out.println(value);
System.out.println(map);
//clear()
map.clear();//與 map = null 操作不同
System.out.println(map.size());
System.out.println(map);
}
@Test
public void test2(){
Map map = new HashMap();
map.put(123, "AA");
map.put(345, "BB");
map.put(12, "CC");
System.out.println(map);
}
@Test
public void test1(){
Map map = new HashMap();
//map = new Hashtable();
map.put(null, 123);
}
}
TreeMap
package com.klvchen.java;
import org.junit.Test;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
public class TreeMapTest {
@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());
}
}
}