看到這個有點懵逼,一時還真不知道怎麼解釋,能讓徹底沒有接觸過的人都能聽懂java
列表,什麼是列表呢?數組
比如你到了一個村裏,看到每一個房子上的門牌號,如這是張村1號,下一個是張村2號,接着是3號、4號...安全
如今你要去張村10號,那麼得,順着這個門牌一直往下走,就能找到了數據結構
這個村落裏的門牌號的形式,就特別像JDK中的 LinkedList
了,找張村10號的過程基本就是 LinkedList#indexOf()
根據索引獲取對應值的過程了併發
從這個case裏面,小結一下List的特色性能
這個順序性好說,這個惟一性是我一家之言,多半描述得不太穩當,出發是爲了與Map進行對比 ,下面重點說明一下線程
以前的博文Java Map常見問題彙總小結 把Map比做了詞典,換在這裏比做村牌號,能夠這麼玩,將戶主名和門牌號一一對應,你報一個戶主名,我就告訴你他家門牌號,而後就有這麼個問題了設計
如今我問張三家門牌是多少?是五號呢仍是105呢?這個就不惟一了code
換成鏈表的方式,你報一個門牌號,要麼這門牌號無效,要麼就只有一家在哪兒等着你呢,這就是我所說的惟一性對象
(廢話比較多,惋惜沒有稿費)
JDK中實現了一些可以覆蓋絕大部分場景的List容器,羅列一些常見的以下
根據是否線程安全分類
線程安全 | 非線程安全 |
---|---|
Vector, CopyOnWriteArrayList | ArrayList, LinkedList |
根據獲取數據的時間複雜度
O(1) | O(n) |
---|---|
ArrayList, Vector, CopyOnWriteArrayList | LinkedList |
這個分類有點意思了,前面三個,給了索引,立馬就把數據給你;這後面的一個,得遍歷一把找到對應的位置再返回數據,詳情建第四條
底層數據結構
數組 | 鏈表 |
---|---|
ArrayList, Vector, CopyOnWriteArrayList | LinkedList |
擴容原理分類
動態擴容 | 無擴容一說 |
---|---|
ArrayList, Vector, CopyOnWriteArrayList | LinkedList |
基於數組的鏈表 ArrayList, 經常使用作有序數據存儲的容器,通常使用的三把斧
// 1. 建立數組 List<String> list = new ArrayList<>(); // 2. 添加數據 list.add("word"); list.add(0, "hello"); // 3. 獲取數組 list.get(0);
實現原理簡要說明
add(obj)
會將數據直接扔進數組中,索引爲當前列表的長度size,而後size+1add(index, obj)
, 在索引處添加數據,會致使原數組中,索引以後的數據後移(即會出現數組拷貝)二者的底層存儲結構不一樣,直接致使最終的使用場景的區別,下面以列表方式給出對比
說明 | ArrayList | LinkedList |
---|---|---|
存儲結構 | ||
隨機訪問 | 根據數組索引定位,耗時 O(1) | 從鏈表頭or鏈表尾遍歷,耗時O(n) |
新增數據 | 列表個數超過數組容量,則數組擴容,出現數組拷貝 | 定位到插入位置,新增一個節點插入鏈表中 |
是否擴容邏輯 | 插入超過上限擴容 <br/> 1. 增長原來空間大小的一半 <br/> 2. 若仍塞不下,則擴充到填滿 | 無擴容邏輯 |
應用場景 | 1. 適合隨機訪問 <br/> 2. 隨機插入or刪除致使數組拷貝 <br/> 3. 末尾插入,較友好 <br/> | 1. 隨機訪問性能差,適合遍歷場景<br/> 2. 隨機插入or刪除,先遍歷獲取位置,而後插入or刪除<br/> 3. 鏈表頭尾插入友好 <br/> |
ArrayList 非線程安全,即在遍歷一個ArrayList對象時,若出現修改,則會拋一個併發修改異常,一般爲了保障線程安全,請使用
CopyOnWriteArrayList
代替,至於Vector,已經退出歷史舞臺了
Vector
線程安全的列表,其實現是在全部的方法上加了同步鎖,確保同一時刻,只有一個線程在訪問列表,是一種僞併發的使用方式
CopyOnWriteArrayList
寫副本,替換原鏈表的方式實現線程安全,基本原理以下
把這個單獨拎出來有點混字數的感受(多謝也沒有啥收益,純屬手欠...)
通常的遍歷方式是採用foreach語法
List<String> strList = new ArrayList<>(); // .... for(String str: strList) { System.out.println(str); }
還有一種用得較少,通常是在要遍歷的過程當中,修改列表的值時使用
List<String> strList = new ArrayList<>(); Iterator<String> iterator = strList.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); if(xxx) { iterator.remove(); } }
那麼問題來了,在第一種方式中,如果修改了會怎麼樣
@Test public void testRemove() { List<String> strList = new ArrayList<String>(); strList.add("hello"); strList.add("world"); strList.add("test1"); strList.add("test2"); strList.remove(0); int i = 0; for(String str: strList) { System.out.println(str); if(++i == 1) { strList.remove(i); } } }
拋了併發修改異常
徹底照着CopyOnWriteArrayList
抄的話就沒意思了,然而讓本身去想一個方案,能夠怎麼搞?實現省略,暫時沒想法。。。