Java集合類,從原始碼解析底層實現原理
總體框架
Java集合總體框架及主要介面,抽象類分析
ArrayList底層實現和原理
Vector底層實現和原理
LinkedList底層實現和原理(也是queue的實現)
ConcurrentLinkedQueue底層實現和原理(常用於併發程式設計)
HashSet底層實現(是由HashMap實現)和原理
TreeSet底層實現(是由TreeMap實現)和原理
HashMap底層實現和原理(1.7陣列+連結串列,1.8陣列+連結串列+紅黑樹)
ConcurrentHashMap底層實現和原理(常用於併發程式設計)
TreeMap底層實現和原理
LinkedHashMap底層實現和原理
hash演算法:一致性hash演算法原理及java實現
下面對上面的文章做一下總結,一些在上面文章中沒有涉及到的點,在詳細的說明一下。
Set和Map的關係
Set代表一種無序不可重複的集合,Map代表一種由多個Key-Value對組成的集合。表面上看它們之間似乎沒有啥關係,但是Map可以看成是Set的擴充套件。為什麼這麼說呢?看下面的這個例子:
在Map的方法中有一個這樣的方法,Set<k> keySet() ,也就是說Map中的鍵可以轉化成一個Set集合。如果把value看成key的一個附屬品,或者把key-value看成是一個整體,那麼Map集合就變成了一個Set集合。
HashSet和HashMap的關係
HashSet和HashMap有很多的相似之處,對於HashSet而言,採用了Hash演算法來決定元素的儲存位置,HashMap而言,將value當成了key的附屬品,根據Key的Hash值來決定存放的位置。
有一點需要說明一下,經常聽說,集合儲存的是物件, 這其實是不準確的。準確來說,集合中儲存的其實是物件的引用地址或者稱為引用變數。而引用地址或者引用變數指向了實際的java物件。java集合實際是引用變數的集合而非java物件的集合。
通過之前的原始碼解析其實可以發現,HashMap在存放key-value時,並沒有過多的考慮value的內容。只是根據key來確定key-value對在陣列中應該存放的位置。HashMap的底層是一個Entry[]陣列,key-value組成了一個entry。當需要向HashMap中新增元素時,首先根據key的hashcode來確定在陣列中存放的位置,如果key為null,採用特殊方法進行處理,存放在陣列的0號位置。如果當前位置已經有元素存在,則遍歷單連結串列,如果兩個key相等,則用新值替換掉舊值,如果key不相等,則插入到連結串列中。有一點需要說明,在jdk8之前,hashmap使用陣列+單連結串列儲存,在8後,採用了陣列+連結串列+紅黑樹儲存。
對於HashSet要說的沒有太多,HashSet的實現也是比較的簡單,它的底層使用HashMap實現的,只是封裝了一個HashMap物件來儲存所有的集合物件。
TreeSet和TreeMap的關係
TreeSet底層採用了一個NavigableMap來儲存TreeSet集合的元素,但實際上NavigableMap只是一個藉口,因為底層依然是使用TreeMap來包含Set集合中的元素。 與HashSet類似,TreeSet也是呼叫TreeMap的方法來實現一些操作。TreeMap的底層是使用“紅黑樹”的排序二叉樹來儲存Map中的每個Entry.關於TreeMap的實現在上面的連結中有詳細的解釋,請自行查閱。
HashSet和HashMap是無序的,而TreeSet和TreeMap是有序的
ArrayList和LinkedList的關係
List代表的是一種線性結構,ArrayList則是一種順序儲存的線性表,ArrayList底層採用陣列來儲存每個元素,LinkedList是一種鏈式儲存的線性表,本質是一個雙向連結串列。
迭代器Iterator
fast-fail快速失敗機制
在迭代的過程中,如果刪除了某一個元素,collection會丟擲ConcurrentModificationException異常。
為什麼會出現這個異常呢?
這是因為在迭代時,某個執行緒對該collection在結構上進行了更改,從而產生fail-fast.當方法檢測到物件修改後,但是不允許這種修改就會丟擲該異常。fail-fast只是一種異常檢測機制,JDK並不能保證該機制一定會發生。
通過一個demo來詳細的說明下:
LinkedList<String> list = new LinkedList<String>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
for (String a : list) {
System.out.println(a);
list.remove(2);
}
執行上面的程式碼便會丟擲 java.util.ConcurrentModificationException;
來看一下LinkedList remove()方法的原始碼:
//刪除方法
public E remove(int index) {
checkElementIndex(index); //驗證index是否合法
return unlink(node(index)); //呼叫unlink方法
}
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++; //modCount+1 敲黑板劃重點
return element;
}
關於上面程式碼的具體含義請自行查閱上面的文章。 上面程式碼在刪除指定位置的元素後將執行私有內部類ListItr中的next()方法,進行下一個元素的遍歷。
//在私有內部類ListItr中有如下的屬性定義,再進行遍歷時,將遍歷物件的modCount值賦值給了expectedModCount。
private int expectedModCount = modCount;
public E next() {
checkForComodification();
if (!hasNext())
throw new NoSuchElementException();
lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.item;
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
執行next()方法後,會先執行checkForComodification()方法,判斷modCount與expectedModCount是否相等,不相等則丟擲異常。
因為是遍歷物件單方面改變的modCount值,ListItr並沒有監測到,所以變造成了modCount和expectedModCount不相等的情況。於是出現了異常。我的理解是,在使用迭代器進行物件遍歷時,建立了一個新的引用,而新引用指向了遍歷的物件,同時將遍歷物件的一些屬性賦值給了迭代器物件。呼叫遍歷物件的方法時,物件的屬性發生變化,而迭代器物件中的遍歷物件的拷貝唯有進行更新,導致了值得不匹配,從而丟擲異常。這只是我的個人理解,歡迎深入交流。
採用下面的方法就不會出現該異常,是因為迭代器物件進行了屬性的更新! 通過Iterator的方法刪除後,保證了modCount與expectedModCount值的統一。
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
String str = iterator.next();
if(str.equals("a")){
iterator.remove();
}
}
相關文章
- Java併發集合類ConcurrentHashMap底層核心原始碼解析JavaHashMap原始碼
- JAVA ArrayList集合底層原始碼分析Java原始碼
- 深度解析HashMap集合底層原理HashMap
- spring原始碼解析 (七) 事務底層原始碼實現Spring原始碼
- ArrayList 從原始碼角度剖析底層原理原始碼
- iOS底層原理總結 -- 利用Runtime原始碼 分析Category的底層實現iOS原始碼Go
- 持久層Mybatis3底層原始碼分析,原理解析MyBatisS3原始碼
- ThreadLocal底層原始碼解析thread原始碼
- ArrayList集合底層原理
- 筆記-集合NSSet、字典NSDictionary的底層實現原理筆記
- Java集合類原始碼Java原始碼
- 七、真正的技術——CAS操作原理、實現、底層原始碼原始碼
- Java阻塞佇列中的異類,SynchronousQueue底層實現原理剖析Java佇列
- NSDictionary底層實現原理
- AutoreleasePool底層實現原理
- HashMap底層實現原理HashMap
- JAVA集合:LinkedList原始碼解析Java原始碼
- 【Java原始碼】集合類-ArrayDequeJava原始碼
- 深入分析Java中的PriorityQueue底層實現與原始碼Java原始碼
- Seata原始碼分析(一). AT模式底層實現原始碼模式
- 探究synchronized底層原理(基於JAVA8原始碼分析)synchronizedJava原始碼
- MySQL索引底層實現原理MySql索引
- ArrayList底層的實現原理
- Java集合Stack原始碼深入解析Java原始碼
- Java集合之Hashtable原始碼解析Java原始碼
- Java 集合Hashtable原始碼深入解析Java原始碼
- Java集合之ArrayList原始碼解析Java原始碼
- Java集合之LinkedList原始碼解析Java原始碼
- 深入原始碼解析 tapable 實現原理原始碼
- Netty原始碼解析 -- PoolChunk實現原理Netty原始碼
- Netty原始碼解析 -- PoolSubpage實現原理Netty原始碼
- 解析ArrayList的底層實現(上)
- MySQL Join的底層實現原理MySql
- Java集合(6)之 HashMap 原始碼解析JavaHashMap原始碼
- Java 集合系列:Vector原始碼深入解析Java原始碼
- HashMap底層資料結構原始碼解析HashMap資料結構原始碼
- Golang WaitGroup 底層原理及原始碼詳解GolangAI原始碼
- InnoDB MVCC實現原理及原始碼解析MVC原始碼