JAVA集合源码
文章目錄
JAVA集合
一、框架圖
二、List
1.ArrayList(部分源碼)
2.LinkedList(部分源碼)
3.Vector(部分源碼)
三、Set
1.HashSet(部分源碼)
2.LinkedHashSet(部分源碼)
3.TreeSet(部分源碼)
四、Queue
1.LinkedList(用作隊列或棧的部分源碼)
2.ArrayDeque(部分源碼)
五、Map
1.HashMap(部分源碼)
2.LinkedHashMap(部分源碼)
3.TreeMap(部分源碼)
4.Hashtable(部分源碼)
總結
JAVA集合
所有集合類都位于java.util包下,支持多線程的集合類位于java.util.concurrent包下。所有的集合框架都包括接口、實現類、算法。實現類就是具體數據結構的實現
一、框架圖
Java集合類主要的兩個根接口,Collection和Map
Collection又派生出List、Set、Queue(Java5新增的隊列)三個子接口
List接口下的常見實現類有ArrayList、LinkedList、Vector
Queue接口下的常見實現類有ArrayDeque、LinkedList
Set接口下的常見實現類有HashSet、LinkedHashSet、SortedSet、TreeSet
Map接口下的常見實現類有HashMap、LinkedHashMap、HashTable、Properties、SortedMap、TreeMap
下面說一下常用常見的實現類(所有源碼均參照JDK11)
二、List
List元素按插入順序,可插入重復元素,能夠通過索引訪問元素
1.ArrayList(部分源碼)
基本屬性
?? ?private static final int DEFAULT_CAPACITY = 10;//默認初始空間
? ? private static final Object[] EMPTY_ELEMENTDATA = {};//空數組
? ? private int size;//ArrayList中的元素個數
?? ?private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//空數組
?? ?transient Object[] elementData; //ArrayList的本質就是該數組
?? ?private int size;//數組中元素數
1
2
3
4
5
6
構造器
?? ?//指定初始容量
?? ?public ArrayList(int initialCapacity) {
? ? ? ? if (initialCapacity > 0) {//指定初始容量大于0
? ? ? ? ? ? this.elementData = new Object[initialCapacity];//創建指定容量數組數組
? ? ? ? } else if (initialCapacity == 0) {//等于構建空數組
? ? ? ? ? ? this.elementData = EMPTY_ELEMENTDATA;
? ? ? ? } else {//小于0拋出異常
? ? ? ? ? ? throw new IllegalArgumentException("Illegal Capacity: "+
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?initialCapacity);
? ? ? ? }
? ? }
? ? //默認創建一個空數組
?? ?public ArrayList() {
?? ? ? ?this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
?? ?}
?? ?//將集合轉為ArrayList
?? ?public ArrayList(Collection<? extends E> c) {
? ? ? ? elementData = c.toArray();//其他集合中的實現方法
? ? ? ? if ((size = elementData.length) != 0) {//要轉換的集合中元素個數不為0
? ? ? ? ? ? if (elementData.getClass() != Object[].class)//當集合中元素不是Object類型時,轉為Object
? ? ? ? ? ? ? ? elementData = Arrays.copyOf(elementData, size, Object[].class);
? ? ? ? } else {//集合中不存在元素,創建空數組
? ? ? ? ? ? this.elementData = EMPTY_ELEMENTDATA;
? ? ? ? }
? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
添加元素
?? ?//在尾部添加元素
? ? public boolean add(E e) {
? ? ? ? modCount++;
? ? ? ? add(e, elementData, size);
? ? ? ? return true;
? ? }
? ? /**
? ? ?* 添加元素的實際操作
? ? ?* JDK的注釋是說將這個方法單獨分離出來,是為了將方法字節碼保持在35以下,
? ? ?* 在C1編譯循環中會有幫助,這個倒沒研究過
? ? ?*/
? ? private void add(E e, Object[] elementData, int s) {
? ? ? ? if (s == elementData.length)//當元素個數等于數組大小,需要擴容
? ? ? ? ? ? elementData = grow();
? ? ? ? elementData[s] = e;//插入元素到數組
? ? ? ? size = s + 1;//元素個數統計
? ? }
?? ?private Object[] grow() {
? ? ? ? return grow(size + 1);
? ? }
?? ?//具體的擴容操作
? ? private Object[] grow(int minCapacity) {
? ? ?? ?//計算新的空間大小,復制數組以達到擴容
? ? ? ? return elementData = Arrays.copyOf(elementData,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?newCapacity(minCapacity));
? ? }
?? ?//計算新空間大小
? ? private int newCapacity(int minCapacity) {
? ? ? ? int oldCapacity = elementData.length;//當前的數組的長度
? ? ? ? int newCapacity = oldCapacity + (oldCapacity >> 1);//新數組的長度,按目標數組長度的右位運算進行擴容(s+ (int)s/2)
? ? ? ? if (newCapacity - minCapacity <= 0) {//擴容后的容量小于等于數組最小容量
? ? ? ? ? ? if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)//如果當前數組是空數組
? ? ? ? ? ? ? ? return Math.max(DEFAULT_CAPACITY, minCapacity);//返回默認初始容量和數組最小容量中的的最大值
? ? ? ? ? ? if (minCapacity < 0) //所需最小容量小于0,拋出異常
? ? ? ? ? ? ? ? throw new OutOfMemoryError();
? ? ? ? ? ? return minCapacity;//返回所需最小容量
? ? ? ? }
? ? ? ? //防止分配空間超過最大分配空間
? ? ? ? return (newCapacity - MAX_ARRAY_SIZE <= 0)
? ? ? ? ? ? ? newCapacity
? ? ? ? ? ? : hugeCapacity(minCapacity);
? ? }
?? ?//當分配空間超過可分配最大空間
?? ?private static int hugeCapacity(int minCapacity) {
? ? ? ? if (minCapacity < 0) // overflow
? ? ? ? ? ? throw new OutOfMemoryError();
? ? ? ? //所需最小空間大于可分配最大空間,返回int最大值,所需最小空間小于可分配的最大空間就返回最大可分配空間
? ? ? ? return (minCapacity > MAX_ARRAY_SIZE)
? ? ? ? ? ? ? Integer.MAX_VALUE
? ? ? ? ? ? : MAX_ARRAY_SIZE;
? ? }
? ??
?? ?//在指定位置插入元素
? ? public void add(int index, E element) {
? ? ?? ?//檢查指定位置是否在集合長度內
? ? ? ? rangeCheckForAdd(index);
? ? ? ? modCount++;
? ? ? ? final int s;
? ? ? ? Object[] elementData;
? ? ? ? if ((s = size) == (elementData = this.elementData).length)
? ? ? ? ?? ?//元素個數等于數組大小時,擴容
? ? ? ? ? ? elementData = grow();
? ? ? ? System.arraycopy(elementData, index,
? ? ? ? ? ? ? ? ? ? ? ? ?elementData, index + 1,
? ? ? ? ? ? ? ? ? ? ? ? ?s - index);
? ? ? ? elementData[index] = element;
? ? ? ? size = s + 1;
? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
刪除元素
?? ?//刪除指定位置元素
?? ?public E remove(int index) {
? ? ? ? Objects.checkIndex(index, size);//檢查指定位置是否在數組長度內
? ? ? ? final Object[] es = elementData;
? ? ? ? @SuppressWarnings("unchecked") E oldValue = (E) es[index];//要刪除的元素
? ? ? ? fastRemove(es, index);//將刪除的元素后面的元素全部左移一位
? ? ? ? return oldValue;
? ? }
? ? //遍歷數組找到該元素第一次出現的位置,將后面的元素全部左移
? ? public boolean remove(Object o) {
? ? ? ? final Object[] es = elementData;
? ? ? ? final int size = this.size;
? ? ? ? int i = 0;
? ? ? ? found: {
? ? ? ? ? ? if (o == null) {//指定元素是null
? ? ? ? ? ? ? ? for (; i < size; i++)
? ? ? ? ? ? ? ? ? ? if (es[i] == null)//第一次出現該元素的位置
? ? ? ? ? ? ? ? ? ? ? ? break found;
? ? ? ? ? ? } else {//指定元素不是null
? ? ? ? ? ? ? ? for (; i < size; i++)
? ? ? ? ? ? ? ? ? ? if (o.equals(es[i]))//第一次出現該元素的位置
? ? ? ? ? ? ? ? ? ? ? ? break found;
? ? ? ? ? ? }
? ? ? ? ? ? return false;//不存在該元素返回false
? ? ? ? }
? ? ? ? fastRemove(es, i);//將刪除的元素后面的元素全部左移一位
? ? ? ? return true;
? ? }
? ? private void fastRemove(Object[] es, int i) {
? ? ? ? modCount++;
? ? ? ? final int newSize;//刪除元素后的數組長度
? ? ? ? if ((newSize = size - 1) > i)//刪除元素不是最后一個元素
? ? ? ? ?? ?//將原數組指定位置開始的元素,復制到原數組,從原數組的要刪除元素的位置開始覆蓋,相當于要刪除元素后面的所有元素全部左移一位
? ? ? ? ? ? System.arraycopy(es, i + 1, es, i, newSize - i);
? ? ? ? es[size = newSize] = null;//刪除元素是最后一個元素,將數組元素數量減一,然后清空該位置元素
? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
ArrayList底層是基于數組實現,默認初始容量是10,擴容大小大約1.5倍,可以直接通過下標取得元素,增加和刪除元素都需要新創建數組效率較低,可以指定初始容量值,避免過多的進行擴容操作
2.LinkedList(部分源碼)
基本屬性
?? ?//鏈表長度(元素個數)
?? ?transient int size = 0;
? ? //指向第一個節點的指針
? ? transient Node<E> first;
? ? //指向最后一個節點的指針
? ? transient Node<E> last;
?? ?//節點結構
?? ?private static class Node<E> {
?? ??? ?//自身
? ? ? ? E item;
? ? ? ? //下一個節點
? ? ? ? Node<E> next;
? ? ? ? //上一個節點
? ? ? ? Node<E> prev;
? ? ? ? Node(Node<E> prev, E element, Node<E> next) {
? ? ? ? ? ? this.item = element;
? ? ? ? ? ? this.next = next;
? ? ? ? ? ? this.prev = prev;
? ? ? ? }
? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
構造器
?? ?//創建一個空LinkedList
?? ?public LinkedList() {}
?? ?//構建包含指定元素的列表
?? ?public LinkedList(Collection<? extends E> c) {
? ? ? ? this();
? ? ? ? addAll(c);
? ? }
? ? public boolean addAll(Collection<? extends E> c) {
? ? ? ? return addAll(size, c);
? ? }
? ? //將指定集合中的元素加入列表中
? ? public boolean addAll(int index, Collection<? extends E> c) {
? ? ? ? checkPositionIndex(index);
? ? ? ? Object[] a = c.toArray();//轉為數組
? ? ? ? int numNew = a.length;//長度(元素個數)
? ? ? ? if (numNew == 0)//不存在元素,返回false
? ? ? ? ? ? return false;
? ? ? ? Node<E> pred, succ;//插入節點的前驅和后繼節點
? ? ? ? if (index == size) {//從尾部開始插入的情況
? ? ? ? ? ? succ = null;//后繼節點為null
? ? ? ? ? ? pred = last;//前驅節點是原來的尾部節點
? ? ? ? } else {//從指定位置開始插入(非尾部)
? ? ? ? ? ? succ = node(index);//獲取index位置的節點,用作插入節點的后繼節點
? ? ? ? ? ? pred = succ.prev;//index位置的節點的前驅節點,用作插入節點的前驅節點
? ? ? ? }
? ? ? ? for (Object o : a) {//遍歷數組元素,插入到鏈表
? ? ? ? ? ? @SuppressWarnings("unchecked") E e = (E) o;
? ? ? ? ? ? Node<E> newNode = new Node<>(pred, e, null);//創建節點
? ? ? ? ? ? if (pred == null)//前驅是null,表示是從頭部開始插入,或者鏈表是null
? ? ? ? ? ? ? ? first = newNode;//新節點用作頭部節點
? ? ? ? ? ? else
? ? ? ? ? ? ? ? pred.next = newNode;//前驅后繼指向新節點
? ? ? ? ? ? pred = newNode;//記錄前驅節點為最新插入的節點
? ? ? ? }
? ? ? ? if (succ == null) {//從尾部開始插入
? ? ? ? ? ? last = pred;//將尾部節點置為最后一個插入節點
? ? ? ? } else {//指定位置開始插入(非尾部)
? ? ? ? ? ? pred.next = succ;//插入的最后一個節點的后繼節點置為原本index位置節點的后繼節點
? ? ? ? ? ? succ.prev = pred;//原本index位置的后繼節點的前驅節點置為插入的最后一個節點
? ? ? ? }
? ? ? ? size += numNew;//鏈表長度
? ? ? ? modCount++;//修改次數加1
? ? ? ? return true;
? ? }
? ? Node<E> node(int index) {
? ? ? ? if (index < (size >> 1)) {//右運算,判斷index位置在linkedlist的前半段,從頭部開始迭代
? ? ? ? ? ? Node<E> x = first;
? ? ? ? ? ? for (int i = 0; i < index; i++)//找到并返回在index位置的節點
? ? ? ? ? ? ? ? x = x.next;
? ? ? ? ? ? return x;
? ? ? ? } else {//判斷index位置在linkedlist的后半段,從尾部開始迭代
? ? ? ? ? ? Node<E> x = last;
? ? ? ? ? ? for (int i = size - 1; i > index; i--)//找到并返回在index位置的節點
? ? ? ? ? ? ? ? x = x.prev;
? ? ? ? ? ? return x;
? ? ? ? }
? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
添加元素
?? ?//將元素添加到列表末尾
?? ?public boolean add(E e) {
? ? ? ? linkLast(e);
? ? ? ? return true;
? ? }
?? ?//將指定元素添加到鏈表尾部
? ? void linkLast(E e) {
? ? ? ? final Node<E> l = last;//原本的尾部節點
? ? ? ? final Node<E> newNode = new Node<>(l, e, null);//新節點
? ? ? ? last = newNode;//尾部節點置為新節點
? ? ? ? if (l == null)//原本的尾部節點是null,鏈表是空,頭結點和尾部節點指向同一節點
? ? ? ? ? ? first = newNode;//頭結點置為新節點
? ? ? ? else
? ? ? ? ? ? l.next = newNode;//原本尾部節點的后繼置為新尾部節點
? ? ? ? size++;//元素加1
? ? ? ? modCount++;//修改次數加1
? ? }
? ? //將元素添加到列表開頭
?? ?public void addFirst(E e) {
? ? ? ? linkFirst(e);
? ? }
? ? //將指定元素插入到頭部
? ? private void linkFirst(E e) {
? ? ? ? final Node<E> f = first;//原本頭節點
? ? ? ? final Node<E> newNode = new Node<>(null, e, f);//新節點(新頭部節點)
? ? ? ? first = newNode;//頭部節點置為新節點
? ? ? ? if (f == null)//原本頭結點是null,鏈表為空,頭節點指針和尾節點指針指向同一節點
? ? ? ? ? ? last = newNode;
? ? ? ? else
? ? ? ? ? ? f.prev = newNode;//將原本頭節點的前驅節點置為新節點
? ? ? ? size++;
? ? ? ? modCount++;
? ? }
? ? //和add方法一樣只是無返回值,(尾部插入元素)
?? ?public void addLast(E e) {
? ? ? ? linkLast(e);
? ? } ? ? ?
? ? //在指點位置插入元素
? ? public void add(int index, E element) {
? ? ? ? checkPositionIndex(index);//檢查指定位置是否在列表長度內
? ? ? ? if (index == size)//指定位置等于鏈表長度,直接插入尾部?
? ? ? ? ? ? linkLast(element);
? ? ? ? else
? ? ? ? ? ? linkBefore(element, node(index));//指定位置插入元素,node(index)表示獲取index位置的節點,參考構造器
? ? }
? ? //指定位置插入元素
? ? void linkBefore(E e, Node<E> succ) {
? ? ? ? final Node<E> pred = succ.prev;//原本index位置的前驅節點,用作新節點的前驅節點
? ? ? ? final Node<E> newNode = new Node<>(pred, e, succ);//新節點,后繼節點為原本index位置的后繼節點
? ? ? ? succ.prev = newNode;//原本index位置節點的前驅節點置為新節點
? ? ? ? if (pred == null)//原本index位置的前驅節點是null,插入到頭部
? ? ? ? ? ? first = newNode;//頭部節點指針置為新節點
? ? ? ? else
? ? ? ? ? ? pred.next = newNode;//原本index 位置節點的前驅節點的后繼節點置為新節點
? ? ? ? size++;
? ? ? ? modCount++;
? ? }
? ??
?? ?//壓棧,鏈表頭部看做棧頂,只在頭部一端做操作
?? ?public void push(E e) {
? ? ? ? addFirst(e);
? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
刪除元素
?? ?//根據指定位置刪除元素
?? ?public E remove(int index) {
? ? ? ? checkElementIndex(index);
? ? ? ? return unlink(node(index));//先獲取指定位置的節點
? ? }
? ? //刪除指定節點
? ? E unlink(Node<E> x) {
? ? ? ? final E element = x.item;//要刪除的元素
? ? ? ? final Node<E> next = x.next;//要刪除節點的后繼節點
? ? ? ? final Node<E> prev = x.prev;//要刪除節點的前驅節點
? ? ? ? if (prev == null) {//前驅節點是null,要刪除的是頭部節點
? ? ? ? ? ? first = next;//頭部節點置為后繼節點
? ? ? ? } else {
? ? ? ? ? ? prev.next = next;//要刪除節點的前驅節點的后繼節點置為要刪除節點的后繼節點
? ? ? ? ? ? x.prev = null;//清空要刪除節點的前驅節點指向
? ? ? ? }
? ? ? ? if (next == null) {//后繼節點是null,要刪除的節點是尾部節點
? ? ? ? ? ? last = prev;//將要刪除節點的前驅節點置為尾部節點
? ? ? ? } else {
? ? ? ? ? ? next.prev = prev;//要刪除節點的后繼節點的前驅節點置為要刪除節點的前驅節點
? ? ? ? ? ? x.next = null;//要刪除節點的后繼節點指向清空
? ? ? ? }
? ? ? ? x.item = null;//要刪除節點的本身元素清空
? ? ? ? size--;
? ? ? ? modCount++;
? ? ? ? return element;//返回刪除的元素
? ? }
? ??
? ? //刪除列表中指定元素(只刪除第一個出現的該元素)
? ? public boolean remove(Object o) {
? ? ? ? if (o == null) {//指定元素是null
? ? ? ? ? ? for (Node<E> x = first; x != null; x = x.next) {//迭代
? ? ? ? ? ? ? ? if (x.item == null) {//找到第一個出現指定元素的節點
? ? ? ? ? ? ? ? ? ? unlink(x);//刪除節點
? ? ? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? } else {
? ? ? ? ? ? for (Node<E> x = first; x != null; x = x.next) {
? ? ? ? ? ? ? ? if (o.equals(x.item)) {
? ? ? ? ? ? ? ? ? ? unlink(x);
? ? ? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return false;
? ? }
? ??
?? ?//彈棧
?? ?public E pop() {
? ? ? ? return removeFirst();//移除棧頂元素
? ? }
? ? public E removeFirst() {
? ? ? ? final Node<E> f = first;//頭部節點
? ? ? ? if (f == null)//頭部節點是null,鏈表為空,拋出異常
? ? ? ? ? ? throw new NoSuchElementException();
? ? ? ? return unlinkFirst(f);
? ? }
? ? //移除頭部節點
? ? private E unlinkFirst(Node<E> f) {
? ? ? ? // assert f == first && f != null;
? ? ? ? final E element = f.item;//要移除元素
? ? ? ? final Node<E> next = f.next;//后繼節點
? ? ? ? f.item = null;//移除節點的元素置為null
? ? ? ? f.next = null; //移除節點的后繼節點置為null ? (方便GC)
? ? ? ? first = next;//將后繼節點置為新的頭部節點
? ? ? ? if (next == null)//更新后繼節點的前驅指向:后繼節點是null,刪除的同時也是尾部節點(只存在一個節點)
? ? ? ? ? ? last = null;//尾部節點置為null
? ? ? ? else
? ? ? ? ? ? next.prev = null;//后繼節點置為了新的頭部節點,前驅指向置為null
? ? ? ? size--;
? ? ? ? modCount++;
? ? ? ? return element;//返回刪除的元素
? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
獲取元素
?? ?//返回列表中第一個出現的該元素的索引
?? ?public int indexOf(Object o) {
? ? ? ? int index = 0;//索引
? ? ? ? if (o == null) {//指定元素是null
? ? ? ? ? ? for (Node<E> x = first; x != null; x = x.next) {//從頭部開始迭代
? ? ? ? ? ? ? ? if (x.item == null)//找到第一個出現的指定元素
? ? ? ? ? ? ? ? ? ? return index;//返回索引
? ? ? ? ? ? ? ? index++;//記錄索引
? ? ? ? ? ? }
? ? ? ? } else {
? ? ? ? ? ? for (Node<E> x = first; x != null; x = x.next) {
? ? ? ? ? ? ? ? if (o.equals(x.item))
? ? ? ? ? ? ? ? ? ? return index;
? ? ? ? ? ? ? ? index++;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return -1;//沒找到返回-1
? ? }
? ? //獲取指定位置元素
? ? public E get(int index) {
? ? ? ? checkElementIndex(index);
? ? ? ? return node(index).item;
? ? }
?? ?//獲取棧頂元素
?? ?public E peek() {
? ? ? ? final Node<E> f = first;//頭部元素
? ? ? ? return (f == null) ? null : f.item;
? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
LinkedList底層基于雙向鏈表實現,同時實現了Deque接口,可以當做雙向隊列使用,也可以當做棧使用,當做棧使用的時候是封住了鏈表尾部只在鏈表頭部進行插入和刪除操作;查找元素需要迭代列表效率較低,在插入和刪除上有較好的性能
3.Vector(部分源碼)
基本屬性
?? ?protected Object[] elementData;//存儲元素的數組
?? ?protected int elementCount;//實際的元素個數
?? ?protected int capacityIncrement;//容量增長系數,小于或等于0將會成倍增長
1
2
3
構造器
?? ?//指定初始容量和需要自動擴容時增加的容量
?? ?public Vector(int initialCapacity, int capacityIncrement) {
? ? ? ? super();
? ? ? ? if (initialCapacity < 0)//初始容量小于0拋出異常
? ? ? ? ? ? throw new IllegalArgumentException("Illegal Capacity: "+
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?initialCapacity);
? ? ? ? this.elementData = new Object[initialCapacity];//創建指定容量的數組
? ? ? ? this.capacityIncrement = capacityIncrement;
? ? }
?? ?public Vector(int initialCapacity) {
? ? ? ? this(initialCapacity, 0);
? ? }
?? ?//默認初始容量10
? ? public Vector() {
? ? ? ? this(10);
? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
添加元素
?? ?//在數組末尾插入元素
?? ?public synchronized boolean add(E e) {
? ? ? ? modCount++;
? ? ? ? add(e, elementData, elementCount);
? ? ? ? return true;
? ? }
? ? private void add(E e, Object[] elementData, int s) {
? ? ? ? if (s == elementData.length)//數組容量到達上限需要擴容
? ? ? ? ? ? elementData = grow();
? ? ? ? elementData[s] = e;//插入元素到數組
? ? ? ? elementCount = s + 1;//元素個數加1
? ? }
? ? private Object[] grow() {
? ? ? ? return grow(elementCount + 1);//數組當前實際元素個數加1,表示數組所需最小容量
? ? }
? ? private Object[] grow(int minCapacity) {
? ? ? ? return elementData = Arrays.copyOf(elementData,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?newCapacity(minCapacity));
? ? }
? ? //計算新空間
? ? private int newCapacity(int minCapacity) {
? ? ? ? int oldCapacity = elementData.length;//數組當前長度
? ? ? ? //計算新長度,capacityIncrement大于0就按capacityIncrement擴容,否則當前長度翻一倍
? ? ? ? int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?capacityIncrement : oldCapacity);
? ? ? ? if (newCapacity - minCapacity <= 0) {//新空間要小于等于所需最小空間
? ? ? ? ? ? if (minCapacity < 0) //所需最小空間驗證
? ? ? ? ? ? ? ? throw new OutOfMemoryError();
? ? ? ? ? ? return minCapacity;//返回最小空間作為新空間大小
? ? ? ? }
? ? ? ? return (newCapacity - MAX_ARRAY_SIZE <= 0)//當新空間大小超過可分配最大空間的處理
? ? ? ? ? ? ? newCapacity
? ? ? ? ? ? : hugeCapacity(minCapacity);
? ? }
? ? //當新空間大小超過可分配最大空間的處理
? ? private static int hugeCapacity(int minCapacity) {
? ? ? ? if (minCapacity < 0)
? ? ? ? ? ? throw new OutOfMemoryError();
? ? ? ? return (minCapacity > MAX_ARRAY_SIZE) ?//所需最小空間要大于可分配最大空間,使用整形最大值,否則使用可分配最大空間
? ? ? ? ? ? Integer.MAX_VALUE :
? ? ? ? ? ? MAX_ARRAY_SIZE;
? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
刪除元素
? ? //移除指定位置元素
? ? public synchronized E remove(int index) {
? ? ? ? modCount++;
? ? ? ? if (index >= elementCount)
? ? ? ? ? ? throw new ArrayIndexOutOfBoundsException(index);
? ? ? ? E oldValue = elementData(index);//獲取指定位置元素
? ? ? ? int numMoved = elementCount - index - 1;//需要移動的元素數量
? ? ? ? if (numMoved > 0)//大于0表示不是末尾元素
? ? ? ? ? ? System.arraycopy(elementData, index+1, elementData, index,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?numMoved);//將指定位置后面的元素全部左移一位
? ? ? ? elementData[--elementCount] = null;// Let gc do its work
? ? ? ? return oldValue;//返回移除的元素
? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
Vector和ArrayList相似,底層也是基于數組實現,不同的是Vector默認初始容量是10,沒有指定擴容系數的話是翻一倍擴容,并且Vector是線程安全的,可以看到在插入元素和刪除元素的方法有使用sychronized關鍵字
三、Set
Set元素插入無序,不能保存重復元素
1.HashSet(部分源碼)
?? ?private transient HashMap<E,Object> map;
? ? private static final Object PRESENT = new Object();//鍵值對用作value的虛擬值
?? ?public HashSet() {
? ? ? ? map = new HashMap<>();
? ? }
? ? public HashSet(int initialCapacity, float loadFactor) {
? ? ? ? map = new HashMap<>(initialCapacity, loadFactor);
? ? }
? ? public HashSet(int initialCapacity) {
? ? ? ? map = new HashMap<>(initialCapacity);
? ? }
? ? HashSet(int initialCapacity, float loadFactor, boolean dummy) {//這個構造器是為LinkedHashSet提供的支持
? ? ? ? map = new LinkedHashMap<>(initialCapacity, loadFactor);
? ? }
? ? public boolean add(E e) {
? ? ? ? return map.put(e, PRESENT)==null;
? ? }
? ? public boolean remove(Object o) {
? ? ? ? return map.remove(o)==PRESENT;
? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
可以看出HashSet的底層就是采用的HashMap來實現的,并且所有提供的方法內部都是通過HashMap操作完成的,可以直接參考HashMap
2.LinkedHashSet(部分源碼)
?? ?public LinkedHashSet(int initialCapacity, float loadFactor) {
? ? ? ? super(initialCapacity, loadFactor, true);
? ? }
? ? public LinkedHashSet(int initialCapacity) {
? ? ? ? super(initialCapacity, .75f, true);
? ? }
? ? public LinkedHashSet() {
? ? ? ? super(16, .75f, true);
? ? }
1
2
3
4
5
6
7
8
9
LinkedHashSet是HashSet子類的底層就是采用的LinkedHashMap來實現的,LinkedHashMap又是HashMap的子類,可以直接參考HashMap和LinkedHashMap
3.TreeSet(部分源碼)
?? ?/**
? ? ?* The backing map.
? ? ?*/
? ? private transient NavigableMap<E,Object> m;
? ? private static final Object PRESENT = new Object();//鍵值對用作value的虛擬值
? ? TreeSet(NavigableMap<E,Object> m) {//該方式TreeSet初始化時 底層是map
? ? ? ? this.m = m;
? ? }
? ? public TreeSet() {
? ? ? ? this(new TreeMap<>());
? ? }
? ? public TreeSet(Comparator<? super E> comparator) {
? ? ? ? this(new TreeMap<>(comparator));
? ? }
? ? public boolean add(E e) {
? ? ? ? return m.put(e, PRESENT)==null;
? ? }
? ? public boolean remove(Object o) {
? ? ? ? return m.remove(o)==PRESENT;
? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
TreeSet底層是基于TreeMap作為容器來實現的,參考TreeMap
四、Queue
Queue代表隊列,是一種特殊的線性表,隊列中只允許在表的前端出隊,后端入隊,又被叫做先進先出(FIFO)線性表;子接口Deque是雙向隊列的實現,雙向隊列是表雙端都可以進行出隊入隊操作,棧是一種后進先出的數據結構,只要將雙向隊列的一端封住那么就可以當做棧來使用了。Deque的實現類有LinkedList和ArrayDeque
1.LinkedList(用作隊列或棧的部分源碼)
?? ?//彈棧
?? ?public E pop() {
? ? ? ? return removeFirst();
? ? }
? ? //壓棧
? ? public void push(E e) {
? ? ? ? addFirst(e);
? ? }
? ?? ?//獲取棧頂元素 ?
? ? public E peek() {
? ? ? ? final Node<E> f = first;
? ? ? ? return (f == null) ? null : f.item;
? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
2.ArrayDeque(部分源碼)
?? ?transient Object[] elements;
?? ?//默認初始容量16
?? ?public ArrayDeque() {
? ? ? ? elements = new Object[16];
? ? }
?? ?//在Deque末尾插入指定元素
?? ?public boolean add(E e) {
? ? ? ? addLast(e);
? ? ? ? return true;
? ? }
? ? public void addLast(E e) {
? ? ? ? if (e == null)
? ? ? ? ? ? throw new NullPointerException();
? ? ? ? final Object[] es = elements;
? ? ? ? es[tail] = e;
? ? ? ? if (head == (tail = inc(tail, es.length)))
? ? ? ? ? ? grow(1);
? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ArrayDeque底層基于數組實現,默認初始容量是16,添加刪除元素和ArrayList有很多相似的地方,可以參考ArrayList,用作隊列或棧的時候,肯定會過多的涉及元素的出棧入棧,更推薦使用LinkedList效率更高,ArrayDeque就沒有著重去看
五、Map
Map接口采用鍵值對具有映射關系的形式存儲數據,可以存儲null,添加元素key存在時會覆蓋value,所有key不能重復,但是value可以重復
1.HashMap(部分源碼)
?? ?//默認初始容量
? ? static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
? ? //最大容量
?? ?static final int MAXIMUM_CAPACITY = 1 << 30;
?? ?//默認的負載因子
?? ?static final float DEFAULT_LOAD_FACTOR = 0.75f;
?? ?//當桶節點數量大于8就會轉為紅黑樹(JDK1.8引入的紅黑樹)
?? ?static final int TREEIFY_THRESHOLD = 8;
?? ?//當桶節點數量小于6就會轉為鏈表,前提條件是當前桶是紅黑樹結構
?? ?static final int UNTREEIFY_THRESHOLD = 6;
?? ?//當HashMap中的所有元素數量大于64,也會轉為紅黑樹
?? ?static final int MIN_TREEIFY_CAPACITY = 64;
?? ?//節點內部類,將一個鍵值對對象化
?? ?static class Node<K,V> implements Map.Entry<K,V> {
? ? ? ? final int hash;//哈希值
? ? ? ? final K key;
? ? ? ? V value;
? ? ? ? Node<K,V> next;
? ? ? ? Node(int hash, K key, V value, Node<K,V> next) {
? ? ? ? ? ? this.hash = hash;
? ? ? ? ? ? this.key = key;
? ? ? ? ? ? this.value = value;
? ? ? ? ? ? this.next = next;
? ? ? ? }
?? ??? ?
? ? ? ? public final int hashCode() {
? ? ? ? ? ? return Objects.hashCode(key) ^ Objects.hashCode(value);
? ? ? ? }
? ? }
?? ?//哈希桶,存放鍵值對元素的數組
?? ?transient Node<K,V>[] table;
?? ?//存放具體元素的集
?? ?transient Set<Map.Entry<K,V>> entrySet;
?? ?//閾值,元素數量超過閾值發生擴容
?? ?int threshold;
?? ?//哈希表的加載因子
?? ?final float loadFactor;
?? ?//紅黑樹
?? ?static final class TreeNode<K, V> extends LinkedHashMap.Entry<K, V> {
? ? ? ? TreeNode<K, V> parent;
? ? ? ? TreeNode<K, V> left;
? ? ? ? TreeNode<K, V> right;
? ? ? ? TreeNode<K, V> prev;
? ? ? ? boolean red;
?
? ? .......
? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
上面是HashMap的一些基本屬性,可以看出底層是一個數組,數組里面存放是一個單向鏈表。
下面從HashMap的創建也就是構造器來看
?? ?public HashMap(int initialCapacity, float loadFactor) {
?? ??? ?//指定的初始容量小于0,拋出異常
? ? ? ? if (initialCapacity < 0)
? ? ? ? ? ? throw new IllegalArgumentException("Illegal initial capacity: " +
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?initialCapacity);
? ? ? ? //指定初始容量超過能分配的最大容量,設置為最大容量 ? ? ? ? ? ? ? ? ? ?
? ? ? ? if (initialCapacity > MAXIMUM_CAPACITY)
? ? ? ? ? ? initialCapacity = MAXIMUM_CAPACITY;
? ? ? ? //如果指定的加載因子為0或者不是數字,拋出異常
? ? ? ? if (loadFactor <= 0 || Float.isNaN(loadFactor))
? ? ? ? ? ? throw new IllegalArgumentException("Illegal load factor: " +
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?loadFactor);
? ? ? ? //設置加載因子 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? this.loadFactor = loadFactor;
? ? ? ? //設置閾值為初始容量的2的n次方
? ? ? ? this.threshold = tableSizeFor(initialCapacity);
? ? }
? ? public HashMap(int initialCapacity) {
? ? ? ? this(initialCapacity, DEFAULT_LOAD_FACTOR);
? ? }
? ? public HashMap() {
? ? ?? ?//設置加載因子為默認加載因子0.75
? ? ? ? this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
? ? }
? ? public HashMap(Map<? extends K, ? extends V> m) {
? ? ? ? this.loadFactor = DEFAULT_LOAD_FACTOR;
? ? ? ? putMapEntries(m, false);
? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
寫入鍵值對的具體方法
?? ?
?? ?public V put(K key, V value) {
? ? ? ? return putVal(hash(key), key, value, false, true);
? ? }
? ? static final int hash(Object key) {
? ? ? ? int h;
? ? ? ? //先獲取key的hashCode值,然后將hashCode無符號右移16位,然后將右移后的值與原來的hashCode值做異或運算
? ? ? ? return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
? ? }
? ??
? ? final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
? ? ? ? ? ? ? ? ? ?boolean evict) {
? ? ? ? Node<K,V>[] tab; Node<K,V> p; int n, i;
? ? ? ? //當前哈希桶是空或者長度是0,就初始化哈希桶
? ? ? ? if ((tab = table) == null || (n = tab.length) == 0)
? ? ? ? ? ? n = (tab = resize()).length;
? ? ? ? if ((p = tab[i = (n - 1) & hash]) == null)//確定元素存放在哪個桶,當該位置沒有元素的時候,新生成節點放入桶中,此時元素存放在數組中
? ? ? ? //其實,(n - 1) & hash 就是取當前hash值和數組容量的余數操作
? ? ? ? ? ? tab[i] = newNode(hash, key, value, null);
? ? ? ? else {//該位置存在元素
? ? ? ? ? ? Node<K,V> e; K k;
? ? ? ? ? ? if (p.hash == hash &&
? ? ? ? ? ? ? ? ((k = p.key) == key || (key != null && key.equals(k))))//與桶中第一個元素hash相等,key相等
? ? ? ? ? ? ? ? e = p;//用e指向第一個元素
? ? ? ? ? ? else if (p instanceof TreeNode)//該位置是紅黑樹,就放入樹中
? ? ? ? ? ? ? ? e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
? ? ? ? ? ? else {//是鏈表
? ? ? ? ? ? ?? ?//在鏈表末端插入節點
? ? ? ? ? ? ? ? for (int binCount = 0; ; ++binCount) {
? ? ? ? ? ? ? ? ?? ?//遍歷到鏈表尾部
? ? ? ? ? ? ? ? ? ? if ((e = p.next) == null) {
? ? ? ? ? ? ? ? ? ? ?? ?//尾部插入新節點
? ? ? ? ? ? ? ? ? ? ? ? p.next = newNode(hash, key, value, null);
? ? ? ? ? ? ? ? ? ? ? ? if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st ?達到節點數量閾值
? ? ? ? ? ? ? ? ? ? ? ? ? ? treeifyBin(tab, hash);//轉換為紅黑樹
? ? ? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? if (e.hash == hash &&
? ? ? ? ? ? ? ? ? ? ? ? ((k = e.key) == key || (key != null && key.equals(k))))//判斷鏈表中節點的key與插入的key是否相等
? ? ? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? ? ? p = e;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? if (e != null) { // 在桶中找到key和hash與插入元素相等的節點
? ? ? ? ? ? ? ? V oldValue = e.value;//記錄桶中的該節點value
? ? ? ? ? ? ? ? if (!onlyIfAbsent || oldValue == null)
? ? ? ? ? ? ? ? ? ? e.value = value;//新值替換舊值
? ? ? ? ? ? ? ? afterNodeAccess(e);//為LinkedHashMap提供支持
? ? ? ? ? ? ? ? return oldValue;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? ++modCount;
? ? ? ? if (++size > threshold)//超過閾值擴容
? ? ? ? ? ? resize();
? ? ? ? afterNodeInsertion(evict);//為LinkedHashMap提供支持
? ? ? ? return null;
? ? }
? ??
? ? //初始化或擴容
? ? final Node<K,V>[] resize() {
? ? ? ? Node<K,V>[] oldTab = table;
? ? ? ? //擴容前的哈希桶長度
? ? ? ? int oldCap = (oldTab == null) ? 0 : oldTab.length;
? ? ? ? //擴容前的閾值
? ? ? ? int oldThr = threshold;
? ? ? ? //擴容后的哈希桶長度和閾值
? ? ? ? int newCap, newThr = 0;
? ? ? ? if (oldCap > 0) {//哈希桶數組不為空
? ? ? ? ? ? if (oldCap >= MAXIMUM_CAPACITY) {//超過所能分配的最大空
? ? ? ? ? ? ? ? threshold = Integer.MAX_VALUE;//就賦值為整數的最大閾值
? ? ? ? ? ? ? ? return oldTab;
? ? ? ? ? ? }
? ? ? ? ? ? else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
? ? ? ? ? ? ? ? ? ? ?oldCap >= DEFAULT_INITIAL_CAPACITY)//雙倍擴容空間后小于所能分配的最大空間,并且大于默認空間16
? ? ? ? ? ? ? ? newThr = oldThr << 1; // 雙倍擴容閾值
? ? ? ? }
? ? ? ? else if (oldThr > 0) //當哈希桶是空,并且閾值大于0,設置初始容量為閾值
? ? ? ? ? ? newCap = oldThr;
? ? ? ? else { ?// 哈希桶是空,初始閾值也是0的時候
? ? ? ? ? ? newCap = DEFAULT_INITIAL_CAPACITY;//設置初始容量為16
? ? ? ? ? ? newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);//閾值為默認加載因子(0.75)*默認空間(16)
? ? ? ? }
? ? ? ? if (newThr == 0) {//新的閾值是0,計算閾值
? ? ? ? ? ? float ft = (float)newCap * loadFactor;
? ? ? ? ? ? newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
? ? ? ? ? ? ? ? ? ? ? (int)ft : Integer.MAX_VALUE);
? ? ? ? }
? ? ? ? threshold = newThr;//設置閾值
? ? ? ? @SuppressWarnings({"rawtypes","unchecked"})
? ? ? ? Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];//創建哈希桶數組
? ? ? ? table = newTab;//引用指向新的哈希桶數組
? ? ? ? if (oldTab != null) {//當舊的哈希桶數組不為空,需要將Node對象值復制到新哈希桶數組
? ? ? ? ? ? for (int j = 0; j < oldCap; ++j) {//遍歷舊的哈希桶
? ? ? ? ? ? ? ? Node<K,V> e;
? ? ? ? ? ? ? ? if ((e = oldTab[j]) != null) {//舊的哈希桶在節點j處不為空,e指向
? ? ? ? ? ? ? ? ? ? oldTab[j] = null;//將舊的哈希桶j節點設為空,方便gc
? ? ? ? ? ? ? ? ? ? if (e.next == null)//后面沒有Node了,也就是沒有元素沖突
? ? ? ? ? ? ? ? ? ? ? ? newTab[e.hash & (newCap - 1)] = e;//對e的哈希值對數組的新長度求模獲取存儲位置
? ? ? ? ? ? ? ? ? ? else if (e instanceof TreeNode)//如果e是紅黑樹類型,添加到紅黑樹
? ? ? ? ? ? ? ? ? ? ? ? ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
? ? ? ? ? ? ? ? ? ? else { // 因為擴容是容量翻倍,所以原鏈表上的每個節點,現在可能存放在原來的下標,即低位, 或者擴容后的下標,即高位。 高位= 低位+原哈希桶容量
? ? ? ? ? ? ? ? ? ? ? ? Node<K,V> loHead = null, loTail = null;//低位鏈表頭尾節點
? ? ? ? ? ? ? ? ? ? ? ? Node<K,V> hiHead = null, hiTail = null;//高位鏈表頭尾節點
? ? ? ? ? ? ? ? ? ? ? ? Node<K,V> next;//臨時節點
? ? ? ? ? ? ? ? ? ? ? ? do {
? ? ? ? ? ? ? ? ? ? ? ? ? ? next = e.next;
? ? ? ? ? ? ? ? ? ? ? ? ? ? //利用哈希值 與 舊的容量,可以得到哈希值去模后,是大于等于oldCap還是小于oldCap,等于0代表小于oldCap,應該存放在低位,否則存放在高位
? ? ? ? ? ? ? ? ? ? ? ? ? ? if ((e.hash & oldCap) == 0) {//節點e的哈希值與舊哈希桶數組的長度做與運算為0
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (loTail == null)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? loHead = e;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? loTail.next = e;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? loTail = e;
? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? ? ? else {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (hiTail == null)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? hiHead = e;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? hiTail.next = e;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? hiTail = e;
? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? } while ((e = next) != null);//循環到鏈表結束
? ? ? ? ? ? ? ? ? ? ? ? if (loTail != null) {//將低位鏈表存放在原index處
? ? ? ? ? ? ? ? ? ? ? ? ? ? loTail.next = null;
? ? ? ? ? ? ? ? ? ? ? ? ? ? newTab[j] = loHead;
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? if (hiTail != null) {//高位鏈表存放在新index處
? ? ? ? ? ? ? ? ? ? ? ? ? ? hiTail.next = null;
? ? ? ? ? ? ? ? ? ? ? ? ? ? newTab[j + oldCap] = hiHead;
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return newTab;
? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
刪除元素部分
?? ?//存在key映射就刪除
?? ?public V remove(Object key) {
? ? ? ? Node<K,V> e;
? ? ? ? return (e = removeNode(hash(key), key, null, false, true)) == null ?
? ? ? ? ? ? null : e.value;
? ? }
? ? final Node<K,V> removeNode(int hash, Object key, Object value,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?boolean matchValue, boolean movable) {
? ? ? ? Node<K,V>[] tab; Node<K,V> p; int n, index;
? ? ? ? if ((tab = table) != null && (n = tab.length) > 0 &&
? ? ? ? ? ? (p = tab[index = (n - 1) & hash]) != null) {//哈希桶不是空,并且key存在哈希桶中,p指向和key同一位置的哈希桶中的第一個元素
? ? ? ? ? ? //node是待刪除節點
? ? ? ? ? ? Node<K,V> node = null, e; K k; V v;
? ? ? ? ? ? if (p.hash == hash &&
? ? ? ? ? ? ? ? ((k = p.key) == key || (key != null && key.equals(k))))//和第一個元素一致
? ? ? ? ? ? ? ? node = p;
? ? ? ? ? ? else if ((e = p.next) != null) {//循環遍歷找到待刪除節點
? ? ? ? ? ? ? ? if (p instanceof TreeNode)//是紅黑樹類型
? ? ? ? ? ? ? ? ? ? node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
? ? ? ? ? ? ? ? else {
? ? ? ? ? ? ? ? ? ? do {
? ? ? ? ? ? ? ? ? ? ? ? if (e.hash == hash &&
? ? ? ? ? ? ? ? ? ? ? ? ? ? ((k = e.key) == key ||
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(key != null && key.equals(k)))) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? node = e;
? ? ? ? ? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? p = e;
? ? ? ? ? ? ? ? ? ? } while ((e = e.next) != null);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? //待刪除節點不是空,且 matchValue為false(如果matchValue為true,則僅在值相等時刪除),或者值相等
? ? ? ? ? ? if (node != null && (!matchValue || (v = node.value) == value ||
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(value != null && value.equals(v)))) {
? ? ? ? ? ? ? ? if (node instanceof TreeNode)//待刪除節點屬于紅黑樹類型
? ? ? ? ? ? ? ? ? ? ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
? ? ? ? ? ? ? ? else if (node == p)//待刪除節點是鏈表頭
? ? ? ? ? ? ? ? ? ? tab[index] = node.next;
? ? ? ? ? ? ? ? else//待刪除節點在表中間
? ? ? ? ? ? ? ? ? ? p.next = node.next;
? ? ? ? ? ? ? ? ++modCount;
? ? ? ? ? ? ? ? --size;
? ? ? ? ? ? ? ? afterNodeRemoval(node);//為LinkedHashMap提供的回調
? ? ? ? ? ? ? ? return node;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return null;
? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
獲取元素,和上面都類似
?? ?public V get(Object key) {
? ? ? ? Node<K,V> e;
? ? ? ? return (e = getNode(hash(key), key)) == null ? null : e.value;
? ? }
? ? final Node<K,V> getNode(int hash, Object key) {
? ? ? ? Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
? ? ? ? if ((tab = table) != null && (n = tab.length) > 0 &&
? ? ? ? ? ? (first = tab[(n - 1) & hash]) != null) {
? ? ? ? ? ? if (first.hash == hash && // always check first node
? ? ? ? ? ? ? ? ((k = first.key) == key || (key != null && key.equals(k))))
? ? ? ? ? ? ? ? return first;
? ? ? ? ? ? if ((e = first.next) != null) {
? ? ? ? ? ? ? ? if (first instanceof TreeNode)//去紅黑樹中取
? ? ? ? ? ? ? ? ? ? return ((TreeNode<K,V>)first).getTreeNode(hash, key);
? ? ? ? ? ? ? ? do {//遍歷鏈表獲取
? ? ? ? ? ? ? ? ? ? if (e.hash == hash &&
? ? ? ? ? ? ? ? ? ? ? ? ((k = e.key) == key || (key != null && key.equals(k))))
? ? ? ? ? ? ? ? ? ? ? ? return e;
? ? ? ? ? ? ? ? } while ((e = e.next) != null);
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return null;
? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
HashMap底層采用數組、鏈表、紅黑樹的方式來實現;初始化默認容量是16,加載因子是0.75,也就是容量達到12就會進行擴容。HashMap中元素key可以存null,但是只能存一個,key不能重復,value可以重復,判斷key重復的標準是根據hash值,如果hash值一致,再根據equals,只有hash和equals都一致的時候才認為重復;當hash值一致,equals不一致,會存儲在數組同一個下標位置(數組中存儲的就是鏈表或者紅黑樹),當鏈表節點數大于8就會轉為紅黑樹,當紅黑樹節點數小于6就會轉為鏈表。擴容是按一倍擴容
2.LinkedHashMap(部分源碼)
?? ?/**
? ? ?* HashMap.Node subclass for normal LinkedHashMap entries.
? ? ?*/
? ? 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);
? ? ? ? }
? ? }
? ??
? ? transient LinkedHashMap.Entry<K,V> head;//雙鏈表頭
? ?
? ? transient LinkedHashMap.Entry<K,V> tail;//雙鏈表尾
? ? //用來指定LinkedHashMap的迭代順序,true表示基于訪問的順序來排列,就是最近使用的Entry放在鏈表的最末尾;false表示基于插入順序
? ? final boolean accessOrder;
?? ?//默認初始容量(16),默認加載因子(0.75)
? ? public LinkedHashMap() {
? ? ? ? super();
? ? ? ? accessOrder = false;
? ? }
? ? //指定初始容量和加載因子
?? ?public LinkedHashMap(int initialCapacity, float loadFactor) {
? ? ? ? super(initialCapacity, loadFactor);
? ? ? ? accessOrder = false;
? ? }
?? ?//插入元素是使用的HashMap的put實現,HashMap中put的是內部類的Node類型節點,該節點不具備和LinkedHashMap內部類Entry和子類型節點組成鏈表的能力,于是重寫了newNode方法
?? ?Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
? ? ? ? LinkedHashMap.Entry<K,V> p =
? ? ? ? ? ? new LinkedHashMap.Entry<>(hash, key, value, e);
? ? ? ? linkNodeLast(p);//將Entry接在雙向鏈表尾部
? ? ? ? return p;
? ? }
? ? // 連接到鏈表末尾(put元素后會回調的方法,維護鏈表結構)
? ? private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
? ? ? ? LinkedHashMap.Entry<K,V> last = tail;
? ? ? ? tail = p;//尾結點為當前插入節點
? ? ? ? if (last == null)//鏈表尾部節點是空,表示鏈表為空,插入第一個節點,頭尾節點是同一個
? ? ? ? ? ? head = p;
? ? ? ? else {
? ? ? ? ? ? p.before = last;//插入節點的前驅指向之前的尾結點
? ? ? ? ? ? last.after = p;//之前尾結點的后繼指向插入的節點
? ? ? ? }
? ? }
?? ?//連接到鏈表末尾(put元素后會回調的方法,維護鏈表結構)
?? ?void afterNodeRemoval(Node<K,V> e) {?
?? ??? ?//獲取要刪除節點的前驅(上一個節點)和后繼(下一個節點)
? ? ? ? LinkedHashMap.Entry<K,V> p =
? ? ? ? ? ? (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
? ? ? ? p.before = p.after = null;//將要刪除節點的前驅和后繼指向清空
? ? ? ? if (b == null)//如果前驅是空,要刪除的就是頭結點
? ? ? ? ? ? head = a;//將要刪除節點的后繼設為頭結點
? ? ? ? else
? ? ? ? ? ? b.after = a;//將前驅的后繼指向要刪除節點的后繼
? ? ? ? if (a == null)//后繼是空,要刪除的是尾結點
? ? ? ? ? ? tail = b;//前驅作為尾結點
? ? ? ? else
? ? ? ? ? ? a.before = b;//將后繼的前驅指向要刪除節點的前驅
? ? }
? ? //重寫了獲取元素方法
?? ?public V get(Object key) {
? ? ? ? Node<K,V> e;
? ? ? ? if ((e = getNode(hash(key), key)) == null)//調用HashMap的getNode方法獲取Node節點
? ? ? ? ? ? return null;
? ? ? ? if (accessOrder)//判斷是按插入順序還是訪問順序
? ? ? ? ? ? afterNodeAccess(e);//按訪問順序的鏈表維護操作
? ? ? ? return e.value;
? ? }
? ? //將節點移到鏈表尾部
? ? void afterNodeAccess(Node<K,V> e) {
? ? ? ? LinkedHashMap.Entry<K,V> last;
? ? ? ? if (accessOrder && (last = tail) != e) {//默認accessOrder是false按插入順序,開啟了順序訪問并且當前訪問節點不是尾結點
? ? ? ? ? ? LinkedHashMap.Entry<K,V> p =
? ? ? ? ? ? ? ? (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;//獲取訪問節點的前驅和后繼
? ? ? ? ? ? p.after = null;//訪問節點要移動到鏈表尾部,后繼指向設成空
? ? ? ? ? ? if (b == null)//前驅是空,訪問的為頭結點
? ? ? ? ? ? ? ? head = a;//將訪問節點的后繼設成頭結點
? ? ? ? ? ? else
? ? ? ? ? ? ? ? b.after = a;//前驅的后繼指向改為訪問節點的后繼
? ? ? ? ? ? if (a != null)//訪問節點的后繼不是空,不是尾結點
? ? ? ? ? ? ? ? a.before = b;//后繼的前驅指向訪問節點的前驅
? ? ? ? ? ? else
? ? ? ? ? ? ? ? last = b;
? ? ? ? ? ? if (last == null)
? ? ? ? ? ? ? ? head = p;
? ? ? ? ? ? else {
? ? ? ? ? ? ? ? p.before = last;//訪問節點的前驅指向原本的尾結點
? ? ? ? ? ? ? ? last.after = p;//原本的尾結點的后繼指向訪問節點
? ? ? ? ? ? }
? ? ? ? ? ? tail = p;//尾部節點設為訪問節點
? ? ? ? ? ? ++modCount;
? ? ? ? }
? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
擴展一下,通過LinkedHashMap實現一個簡單的LRU緩存策略。
首先什么是LRU緩存策略,這是一種緩存淘汰機制,我們的緩存都是有限的,緩存滿了的情況下,怎么給新內容騰位置。LRU緩存策略就是我們認為最近使用的數據是有用的,許久不用的數據是無用的,優先刪除許久不用的數據
在LinkedHashMap源碼中有下面的方法為LRU的實現提供了支持
?? ?//改方法在put元素的時候有被調用,參數【evict】官方解釋是,false的時候表屬于創建模式
?? ?void afterNodeInsertion(boolean evict) {?
? ? ? ? LinkedHashMap.Entry<K,V> first;
? ? ? ? //表不是創建模式(表已經創建),并且頭結點不是空(表中有元素),并且自定義策略通過的時候,移除節點
? ? ? ? if (evict && (first = head) != null && removeEldestEntry(first)) {
? ? ? ? ? ? K key = first.key;
? ? ? ? ? ? removeNode(hash(key), key, null, false, true);
? ? ? ? }
? ? }
? ? //通過LinkedHashMap來實現LRU,可以通過重寫該方法實現自定義的LRU緩存策略
? ? protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
? ? ? ? return false;
? ? }
?? ?//重寫了父類方法,和get差不多,只是在表中沒有對應鍵值對的時候,返回的是我們的默認值
? ? public V getOrDefault(Object key, V defaultValue) {
? ? ? ?Node<K,V> e;
? ? ? ?if ((e = getNode(hash(key), key)) == null)
? ? ? ? ? ?return defaultValue;
? ? ? ?if (accessOrder)
? ? ? ? ? ?afterNodeAccess(e);
? ? ? ?return e.value;
? ?}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
通過繼承LinkedHashMap來實現LRU
class LRUCache extends LinkedHashMap<String,String>{
?? ?private int capacity;//容量
?? ?
? ? public LRUCache(int capacity) {
? ? ? ? super(capacity, 0.75F, true);//使用默認加載因子0.75,自定義指定容量
? ? ? ? this.capacity = capacity;
? ? }
? ??
? ? public int get(int key) {
? ? ? ? return super.getOrDefault(key, -1);
? ? }
? ??
? ? public void put(int key, int value) {
? ? ? ? super.put(key, value);
? ? }
? ? /**
? ? * 判斷節點數是否超限
? ? * @param eldest
? ? * @return 超限返回 true, 否則返回false
? ? */
? ? @Override
? ? protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
? ? ? ? return size() > capacity;
? ? }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
LinkedHashMap是HashMap的子類,繼承了父類的屬性,在添加和刪除元素LinkedHashMap都是直接使用的父類的方法,不同的是LinkedHashMap采用了雙向鏈表維護元素順序,重寫了用來維護鏈表的回調方法
3.TreeMap(部分源碼)
?? ?//用于維護TreeMap中的順序的比較器,使用鍵的自然排序它就是null
?? ?private final Comparator<? super K> comparator;
?? ?//根節點
?? ?private transient Entry<K,V> root;
?? ?//Entry樹的節點類
?? ?static final class Entry<K,V> implements Map.Entry<K,V> {
? ? ? ? K key;//鍵
? ? ? ? V value;//值
? ? ? ? Entry<K,V> left;//左節點
? ? ? ? Entry<K,V> right;//右節點
? ? ? ? Entry<K,V> parent;//父節點
? ? ? ? boolean color = BLACK;
? ? ? ??
? ? ? ? Entry(K key, V value, Entry<K,V> parent) {
? ? ? ? ? ? this.key = key;
? ? ? ? ? ? this.value = value;
? ? ? ? ? ? this.parent = parent;
? ? ? ? }
?? ?}
?? ?
?? ?//創建默認的TreeMap,默認使用鍵的自然排序;前提是插入到TreeMap中的所有鍵都必須實現了Comparator接口
?? ?public TreeMap() {
? ? ? ? comparator = null;
? ? }
? ? //創建的TreeMap根據給定的排序器排序,如果為null就按自然排序
? ? public TreeMap(Comparator<? super K> comparator) {
? ? ? ? this.comparator = comparator;
? ? }
? ? //創建新的TreeMap并將指定map中的元素添加到TreeMap
?? ?public TreeMap(Map<? extends K, ? extends V> m) {
? ? ? ? comparator = null;
? ? ? ? putAll(m);
? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
從上面可以看出來TreeMap的底層是紅黑樹來實現的,下面看看TreeMap是如何添加元素的
?? ?public V put(K key, V value) {
? ? ? ? Entry<K,V> t = root;//根節點
? ? ? ? if (t == null) {//根節點是null,TreeMap里面沒有元素
? ? ? ? ? ? compare(key, key); // type (and possibly null) check
? ? ? ? ? ? root = new Entry<>(key, value, null);//新建節點,設置成根節點
? ? ? ? ? ? size = 1;
? ? ? ? ? ? modCount++;
? ? ? ? ? ? return null;
? ? ? ? }
? ? ? ? int cmp;
? ? ? ? Entry<K,V> parent;//父節點
? ? ? ??
? ? ? ? Comparator<? super K> cpr = comparator;// 比較器
? ? ? ? if (cpr != null) {//按比較器排序,循環查找key要插入的位置
? ? ? ? ? ? do {
? ? ? ? ? ? ? ? parent = t;//記錄循環的節點,最后循環到的節點就是要插入節點的父節點
? ? ? ? ? ? ? ? cmp = cpr.compare(key, t.key);//比較新插入的key和當前節點key的大小
? ? ? ? ? ? ? ? if (cmp < 0)//新插入的key要小
? ? ? ? ? ? ? ? ? ? t = t.left;//將當前節點的左子節點作為新的比較節點
? ? ? ? ? ? ? ? else if (cmp > 0)//新插入的key要大
? ? ? ? ? ? ? ? ? ? t = t.right;//將當前節點的右子節點作為新的比較節點
? ? ? ? ? ? ? ? else//當前節點key和新插入的key相等,覆蓋value
? ? ? ? ? ? ? ? ? ? return t.setValue(value);
? ? ? ? ? ? } while (t != null);//t是null,表示已經沒有可以比較的節點,已經找到插入位置
? ? ? ? }
? ? ? ? else {//按自然排序,循環查找key要插入的位置
? ? ? ? ? ? if (key == null)//比較器是null,需要使用key作為比較器來進行比較,并且key必須實現了Comparable接口
? ? ? ? ? ? ? ? throw new NullPointerException();
? ? ? ? ? ? @SuppressWarnings("unchecked")
? ? ? ? ? ? ? ? Comparable<? super K> k = (Comparable<? super K>) key;
? ? ? ? ? ? do {
? ? ? ? ? ? ? ? parent = t;//記錄循環節點,最后循環到的節點就是要插入節點的父節點
? ? ? ? ? ? ? ? cmp = k.compareTo(t.key);//根據實現的compareto方法來比較當前節點key和新插入key的大小
? ? ? ? ? ? ? ? if (cmp < 0)//新插入的key要小
? ? ? ? ? ? ? ? ? ? t = t.left;//將//新插入的key要大當前節點的左子節點作為新的比較節點
? ? ? ? ? ? ? ? else if (cmp > 0)//新插入的key要大
? ? ? ? ? ? ? ? ? ? t = t.right;//將當前節點的右子節點作為新的比較節點
? ? ? ? ? ? ? ? else//當前節點key和新插入的key相等,覆蓋value
? ? ? ? ? ? ? ? ? ? return t.setValue(value);
? ? ? ? ? ? } while (t != null);//t是null,表示已經沒有可以比較的節點,已經找到插入位置
? ? ? ? }
? ? ? ? Entry<K,V> e = new Entry<>(key, value, parent);//新建節點
? ? ? ? if (cmp < 0)//新節點key要小于父節點的key,插入父節點的左側
? ? ? ? ? ? parent.left = e;
? ? ? ? else新節點key要大于等于父節點的key,插入父節點的右側
? ? ? ? ? ? parent.right = e;
? ? ? ? fixAfterInsertion(e);//新插入節點,為了保持紅黑樹平衡需要對樹進行調整(這一步涉及紅黑樹操作比較復雜,這里不做闡述)
? ? ? ? size++;
? ? ? ? modCount++;
? ? ? ? return null;
? ? }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
4.Hashtable(部分源碼)
?? ?//哈希表數據
? ? private transient Entry<?,?>[] table;
?? ?//哈希表條目總數
? ? private transient int count;
? ? //閾值
?? ?private int threshold;
?? ?//加載因子
?? ?private float loadFactor;
?? ?//指定初始容量和加載因子
?? ?public Hashtable(int initialCapacity, float loadFactor) {
? ? ? ? if (initialCapacity < 0)
? ? ? ? ? ? throw new IllegalArgumentException("Illegal Capacity: "+
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?initialCapacity);
? ? ? ? if (loadFactor <= 0 || Float.isNaN(loadFactor))
? ? ? ? ? ? throw new IllegalArgumentException("Illegal Load: "+loadFactor);
? ? ? ? if (initialCapacity==0)
? ? ? ? ? ? initialCapacity = 1;
? ? ? ? this.loadFactor = loadFactor;
? ? ? ? table = new Entry<?,?>[initialCapacity];
? ? ? ? threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
? ? }
? ? //指定初始容量,默認加載因子
? ? public Hashtable(int initialCapacity) {
? ? ? ? this(initialCapacity, 0.75f);
? ? }
?? ?//默認初始容量11,加載因子0.75
?? ?public Hashtable() {
? ? ? ? this(11, 0.75f);
? ? }
?? ?private static class Entry<K,V> implements Map.Entry<K,V> {
? ? ? ? final int hash;
? ? ? ? final K key;
? ? ? ? V value;
? ? ? ? Entry<K,V> next;
? ? ? ? protected Entry(int hash, K key, V value, Entry<K,V> next) {
? ? ? ? ? ? this.hash = hash;
? ? ? ? ? ? this.key = ?key;
? ? ? ? ? ? this.value = value;
? ? ? ? ? ? this.next = next;
? ? ? ? }
? ? ?}
添加元素部分
?? ?public synchronized V put(K key, V value) {
? ? ? ? if (value == null) {//值不能為null
? ? ? ? ? ? throw new NullPointerException();
? ? ? ? }
? ? ? ? // Makes sure the key is not already in the hashtable.
? ? ? ? Entry<?,?> tab[] = table;
? ? ? ? int hash = key.hashCode();//獲取key的哈希值
? ? ? ? int index = (hash & 0x7FFFFFFF) % tab.length;//通過哈希值找到存儲位置
? ? ? ? @SuppressWarnings("unchecked")
? ? ? ? Entry<K,V> entry = (Entry<K,V>)tab[index];//取得該位置的entry對象
? ? ? ? for(; entry != null ; entry = entry.next) {//遍歷該位置鏈表
? ? ? ? ? ? if ((entry.hash == hash) && entry.key.equals(key)) {//key相同,覆蓋舊值
? ? ? ? ? ? ? ? V old = entry.value;
? ? ? ? ? ? ? ? entry.value = value;
? ? ? ? ? ? ? ? return old;
? ? ? ? ? ? }
? ? ? ? }
?? ??? ?//添加entry對象
? ? ? ? addEntry(hash, key, value, index);
? ? ? ? return null;
? ? }
? ? private void addEntry(int hash, K key, V value, int index) {
? ? ? ? Entry<?,?> tab[] = table;
? ? ? ? if (count >= threshold) {//當前容量超過閾值,需要擴容按2n+1
? ? ? ? ? ? rehash();
? ? ? ? ? ? tab = table;
? ? ? ? ? ? hash = key.hashCode();
? ? ? ? ? ? index = (hash & 0x7FFFFFFF) % tab.length;//取模運算
? ? ? ? }
? ? ? ? @SuppressWarnings("unchecked")
? ? ? ? Entry<K,V> e = (Entry<K,V>) tab[index];//取得該位置的entry對象
? ? ? ? tab[index] = new Entry<>(hash, key, value, e);//創建新節點,新節點插入鏈表首部
? ? ? ? count++;
? ? ? ? modCount++;
? ? }
? ? protected void rehash() {
? ? ? ? int oldCapacity = table.length;//當前哈希表長度
? ? ? ? Entry<?,?>[] oldMap = table;//舊哈希表
? ? ? ? int newCapacity = (oldCapacity << 1) + 1;//新容量,相當于2n+1
? ? ? ? if (newCapacity - MAX_ARRAY_SIZE > 0) {//新容量超過可分配最大空間,默認使用最大空間
? ? ? ? ? ? if (oldCapacity == MAX_ARRAY_SIZE)
? ? ? ? ? ? ? ? // Keep running with MAX_ARRAY_SIZE buckets
? ? ? ? ? ? ? ? return;
? ? ? ? ? ? newCapacity = MAX_ARRAY_SIZE;
? ? ? ? }
? ? ? ? Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];//創建新容量大小的哈希表
? ? ? ? modCount++;
? ? ? ? threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);//閾值
? ? ? ? table = newMap;//引用指向
? ? ? ? for (int i = oldCapacity ; i-- > 0 ;) {//將舊哈希表中的元素復制到新哈希表
? ? ? ? ? ? for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
? ? ? ? ? ? ? ? Entry<K,V> e = old;
? ? ? ? ? ? ? ? old = old.next;
? ? ? ? ? ? ? ? int index = (e.hash & 0x7FFFFFFF) % newCapacity;
? ? ? ? ? ? ? ? e.next = (Entry<K,V>)newMap[index];
? ? ? ? ? ? ? ? newMap[index] = e;
? ? ? ? ? ? }
? ? ? ? }
? ? }
移除元素部分
?? ?public synchronized V remove(Object key) {
? ? ? ? Entry<?,?> tab[] = table;//當前哈希表
? ? ? ? int hash = key.hashCode();//key的哈希值
? ? ? ? int index = (hash & 0x7FFFFFFF) % tab.length;//取模,計算key在哈希表的位置
? ? ? ? @SuppressWarnings("unchecked")
? ? ? ? Entry<K,V> e = (Entry<K,V>)tab[index];//當前位置的鏈表首部entry
? ? ? ? for(Entry<K,V> prev = null ; e != null ; prev = e, e = e.next) {//遍歷鏈表
? ? ? ? ? ? if ((e.hash == hash) && e.key.equals(key)) {//找到鏈表中哈希值和equals都相同的節點
? ? ? ? ? ? ? ? if (prev != null) {//要移除節點的上一個節點不是null,表示不是頭部,將后繼指向修改
? ? ? ? ? ? ? ? ? ? prev.next = e.next;
? ? ? ? ? ? ? ? } else {//移除節點是頭部,將移除節點的后繼節點用作鏈表頭部,放入哈希表當前位置
? ? ? ? ? ? ? ? ? ? tab[index] = e.next;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? modCount++;
? ? ? ? ? ? ? ? count--;
? ? ? ? ? ? ? ? V oldValue = e.value;
? ? ? ? ? ? ? ? e.value = null;//清空值
? ? ? ? ? ? ? ? return oldValue;//返回移除key對應的value
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return null;//沒找到對應key,返回null
? ? }
獲取元素
?? ?public synchronized V get(Object key) {
? ? ? ? Entry<?,?> tab[] = table;
? ? ? ? int hash = key.hashCode();
? ? ? ? int index = (hash & 0x7FFFFFFF) % tab.length;
? ? ? ? for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {//遍歷該位置鏈表
? ? ? ? ? ? if ((e.hash == hash) && e.key.equals(key)) {//找到對應的entry對象
? ? ? ? ? ? ? ? return (V)e.value;//返回值
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return null;//沒找到返回null
? ? }
Hashtable和HashMap類似,都是采用哈希表和鏈表來實現。不同的是,
1.在構建的時候,Hashtable的默認初始容量是11,HashMap是16
2.添加元素的時候,Hashtable對key和value做了null驗證,保證了key和value不能為null,而HashMap是可以插入null鍵值對的
3.擴容的時候,Hashtable是按2n+1,HashMap是2n
4.在獲取元素和移除元素的方法上Hashtable是有synchronized 關鍵字的,其他讀寫元素的方法也有,Hashtable是線程安全的
總結
List:元素有序、可重復集合,可通過索引訪問元素
ArrayList:底層基于數組實現,默認初始容量是10,擴容的時候按左位移運算擴容,每次擴容都需要創建數組,可以指定合適的初始化容量避免多次擴容。基于數組的原因,在隨機訪問上效率高;當插入元素到數組中間位置的時候,該位置后面的元素也需要全部后移一位,刪除元素的時候需要前移,涉及多個元素操作的時候插入刪除效率就會偏低(并且擴容操作也在插入的時候進行)
LinkedList:基于雙向鏈表實現,隨機訪問元素效率要差,插入刪除元素效率要高
Vector:基于數組實現,和ArrayList相似,初始容量也是10,操作也基本相同,不同的是擴容默認按一倍擴容,并且它是線程安全的
Set:元素不可重復
HashSet:基于HashMap來實現
LinkedHashSet:HashSet的子類,基于LinkedHashMap實現
TreeSet:基于TreeMap實現
Map:存儲鍵值對,key不允許重復
HashMap:不保證元素的插入順序,初始容量16,默認加載因子0.75,采用數組、鏈表、紅黑樹來實現,數組中存儲的是鏈表或者紅黑樹,當鏈表節點數大于8就會轉為紅黑樹,紅黑樹節點數小于6就會轉為鏈表,按一倍擴容,通過哈希值和equals來判斷key是否重復
LinkedHashMap:HashMap的子類,在HashMap的基礎上添加了鏈表來維護元素的順序,LinkedHashMap中的accessOrder屬性默認是false,也就是默認維護元素的插入順序,當設置true的時候維護元素的訪問順序,也就是get過后,會將訪問的元素移動到鏈表尾部,需要維護元素順序,性能比HashMap低
TreeMap:一個有序的鍵值對集合,不能存儲null,基于紅黑樹實現,有兩種排序方式,自然排序和定制排序
自然排序:所有key必須實現Comparable接口,并且所有的key應該是同一個類得對象,否則會拋出ClassCastException
定制排序:創建TreeMap時,傳入一個Comparator對象,該對象負責對TreeMap中的所有key進行排序
————————————————
版權聲明:本文為CSDN博主「紅了個籮蘿卜」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_42432033/article/details/118359434
總結
- 上一篇: Collection 和 Collect
- 下一篇: shiro学习(17):easyui布局