1.概念
1.1.集合與數組的區別
集合:長度不固定,動態的根據數據添加刪除改變長度,并且只能存入引用類型,讀取采用迭代器或其他方法
數組:長度固定,不可改變,既可以存入基本類型也可以存入引用類型,讀取使用索引讀(for)
長度 | 存入類型 | 讀取 | |
集合 | 長度不固定,動態的根據數據添加刪除改變長度 | 只能存入引用類型 | 采用迭代器或其他方法 |
數組 | 長度固定,不可改變 | 既可以存入基本類型也可以存入引用類型 | 使用索引(for) |
1.2.集合分類
分為三類:List類,Set類,Map類
List集合:集合里面元素有序,并且允許可重復
Set集合:集合里面元素無序,并且不可重復(保證唯一性)
Map集合:集合采用鍵值對方式,key唯一(不允許重復)無序,value沒有要求
是否有序 | 是否可重復 | |
List | 有序 | 可重復 |
Set | 無序 | 不可重復 |
1.3.Collection和Collections的區別
Collection是一個接口,給集合實現的,里面定義了一些操作集合的方法
Collections是一個工具類,位于java.util包中,可以直接使用該類操作集合(增刪改,排序)
1.4.集合遍歷的方法
有六種方法:for,增強for,迭代器,列表迭代器,foeEach,Stream流
for:帶索引查詢(區分集合是否帶索引,才能使用該方法)
List<String> list = Arrays.asList("A", "B", "C");// 通過索引遍歷(適合 ArrayList 等支持隨機訪問的集合)
for (int i = 0; i < list.size(); i++) {System.out.println(list.get(i));
}
增強for:沒有索引,直接遍歷查詢
List<String> list = Arrays.asList("A", "B", "C");// 直接遍歷元素(底層基于迭代器實現)
for (String item : list) {System.out.println(item);
}
迭代器:在迭代器里面只能刪除元素,不能插入元素
List<String> list = Arrays.asList("A", "B", "C");
Iterator<String> iterator = list.iterator();// 通過迭代器遍歷(適用于所有 Collection)
while (iterator.hasNext()) {String item = iterator.next();System.out.println(item);// 可在遍歷中安全刪除元素:iterator.remove();
}
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
Iterator<String> iterator = list.iterator();while (iterator.hasNext()) {String item = iterator.next();if ("B".equals(item)) {iterator.remove(); // 允許刪除當前元素// iterator.add("D"); // 編譯錯誤:Iterator 沒有 add() 方法}
}
列表迭代器:沒有限制,可以進行刪除查詢插入元素
List<String> list = Arrays.asList("A", "B", "C");
ListIterator<String> listIterator = list.listIterator();// 正向遍歷(從頭到尾)
while (listIterator.hasNext()) {String item = listIterator.next();System.out.println(item);
}// 反向遍歷(從尾到頭)
while (listIterator.hasPrevious()) {String item = listIterator.previous();System.out.println(item);
}
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
ListIterator<String> listIterator = list.listIterator();// 正向遍歷
while (listIterator.hasNext()) {String item = listIterator.next();if ("B".equals(item)) {listIterator.remove(); // 刪除當前元素listIterator.add("D"); // 在當前位置插入新元素listIterator.set("E"); // 替換當前元素(需在 next() 或 previous() 后調用)}
}// 反向遍歷
while (listIterator.hasPrevious()) {String item = listIterator.previous();System.out.println(item);
}
forEach:因為它基于迭代器實現的,因此也不能在循環中插入元素
List<String> list = Arrays.asList("A", "B", "C");// 使用 Lambda 表達式遍歷
list.forEach(item -> System.out.println(item));// 或使用方法引用
list.forEach(System.out::println);
Stream:沒有限制
List<String> list = Arrays.asList("A", "B", "C");// 轉換為 Stream 并遍歷
list.stream().forEach(item -> System.out.println(item));// 并行流遍歷(多線程處理)
list.parallelStream().forEach(item -> System.out.println(item));
2.List
2.1.List的實現
實現List的集合有:ArrayList,LinkedList,Vector
ArrayList:基于動態的數組創建的,查詢效率高,增刪效率一般,線程不安全
LinkedList:基于雙向鏈表創建的,查詢效率一般,增刪效率高,線程不安全
Vector:基于動態數組創建的,與ArrayList類似,不過它是線程安全的
數據結構 | 讀操作 | 寫操作 | 線程安全 | |
ArrayList | 數組 | 高 | 一般 | 不安全 |
LinkedList | 雙向鏈表 | 一般 | 高 | 不安全 |
Vector | 數組 | 高 | 一般 | 安全 |
2.2.可以一邊遍歷一邊修改List的方法
首先思考有幾個遍歷方法:六個
哪些是不能修改元素的:迭代器,forEach
最終得到的方法:for,增強for,列表迭代器,Stream流
2.3.List快速刪除元素的原理
原理是基于集合底層數據結構不同,分為兩類:ArrayList,LinkedList
ArrayList:基于數組對吧,原先數組是通過索引刪除數據,那么因此ArrayList也是如此,基于索引來刪除數據
具體實現:如果你是刪除尾部最后一個數據,直接刪除即可,時間復雜度為O(1),如果不是,那么它會將索引元素刪除后,將后面的元素往前面覆蓋,然后計算出集合長度,時間復雜度為O(n),n為元素的個數
LinkedList:基于雙向鏈表,簡單來說鏈表由節點組成,每個節點包含自己的數據與前一個節點的引用和后一個節點的引用,實現雙向并通
具體實現:如果你是刪除尾部最后一個數據,直接刪除即可,時間復雜度為O(1),如果不是,那么就是從頭或尾進行查詢刪除,時間復雜度O(n)
2.4.ArrayList與LinkedList的區別
- 數據結構組成不同:Array List基于數組,LinkedList基于雙向鏈表
- 刪除和插入效率不同:ArrayList在尾部的效率高(平均O(1)),在其他的地方效率低,由于需要進行元素覆蓋,而LinkedList它基于鏈表引用,在尾部的效率(O(1)比ArrayList效率低(ArrayList基于數組,內存是連續的,而LinkedList基于鏈表,內存不連續),在其他地方刪除與插入與ArrayList效率差不多(O(n))
- 隨機訪問速度:由于ArrayList基于數組根據索引查詢,時間復雜度O(1),而LinkedList基于鏈表,它需要從頭或尾部訪問,因此時間復雜度為O(n)
- 適用場景不同:ArrayList更適合高頻的隨機訪問操作或尾部插入為主,LinkedList更適合高頻頭尾插入/刪除(隊列)或需要雙向遍歷
- 線程安全:都是線程不安全的
2.5.線程安全
實現線程(List)安全的方法有:
- 實現Collections.synchronizedList,將線程不安全的List集合加個鎖,變成安全的
- 直接使用線程安全的List集合:比如Vector,CopyOnWirteArrayList
2.6.ArrayList的擴容機制
首先如果你沒有指定長度,默認長度為10,當你要添加元素并且超過此時容量長度時,就會進行擴容操作
實現:
1.擴容:創建一個新的數組,新數組的長度為原數組的1.5倍數,然后再檢查容量是否足夠,不夠繼續擴容
---
2.復制:將舊的數組里面的值復制進新的數組中,再進行寫操作
---
3.更改引用:將原先指向舊數組的引用指向新數組
---
4.擴容完成:可以繼續擴容
2.7.CopyOnWirteArrayList
它實現了讀寫分離,寫操作加了互斥鎖ReentrantLock,避免出現線程安全問題,而讀操作沒有加鎖,使用volatile關鍵字修飾數組,保證當前線程對數組對象重新賦值后,其他線程可以及時感知到(所有線程可見性)。線程讀取數據可以直接讀取,提高效率
寫操作:它不會向ArrayList一樣直接擴容1.5倍,它是根據你的添加元素個數多少來擴容,如果你只添加一個元素,那么它會創建一個新數組,長度比舊數組長度多一,然后依舊是依次復制元素進新數組中,改變內部引用指向(需要頻繁創建新的數組,以時間換空間)
讀操作:就是說它不會管你的數據是否修改,內部指向是舊數組,那么就讀取舊數組的數據,指向是新數組就讀取新數據,這樣效率會高(數據弱一致性)