集合:List介面的實現類(ArrayList、LinkedList、Vector)
1、List介面
(1)特點
- 有序(插入和取出的順序相等,因為有一個整數索引記錄了元素的插入的位置)
- 允許有重複的元素(調用equals方法返回true,允許有多個null)
@Test public void test1() { List list=new ArrayList(); list.add("123"); list.add("123"); list.add(null); list.add(111); list.add(null); System.out.println(list); }
[123, 123, null, 111, null]
(2)與collection相比的特有方法
- add(index,元素):在指定的位置插入
- get(index):獲取指定位置的元素
- remove(index):刪除指定位置的元素
- set(index,新元素):修改指定位置的元素
- subList(index,index):根據索引範圍截取一個子集合
- indexOf(元素):查找
與collection介面的最大區別就是list介面的操作會涉及到索引。
2、List介面的特有方法
(1)增加元素
指定位置插入
@Test public void test1() { List list=new ArrayList(); list.add("123"); list.add("123"); list.add(null); list.add(111); list.add(null); list.add(1,"zhai"); System.out.println(list); }
[123, zhai, 123, null, 111, null]
(2)下標越界異常
@Test public void test1() { List list=new ArrayList(); list.add(1,"zhai"); System.out.println(list); }
java.lang.IndexOutOfBoundsException: Index: 1, Size: 0
程式源碼:
判斷索引的範圍是否有效:
索引大於集合大小或索引小於0,拋出異常:
(2)刪除元素
根據索引刪除:
@Test public void test1() { List list=new ArrayList(); list.add("123"); list.add("123"); list.add(null); list.add(111); list.add(null); list.remove(2); System.out.println(list); }
[123, 123, 111, null]
默認按照索引刪除:
@Test public void test1() { List list=new ArrayList(); list.add("123"); list.add("123"); list.add(8848); list.add(111); list.add(1); list.remove(1); System.out.println(list); }
[123, 8848, 111, 1]
在可以按照索引刪除和按照元素刪除的情況下,List默認按照索引刪除元素
編譯器提示的也是按照索引刪除,因為不存在100索引,會拋出異常:
java.lang.IndexOutOfBoundsException: Index: 100, Size: 5
查看源碼可以看出remove存在方法的重載,index是基本數據類型的數據,如果是按照內容移除還要講int類型轉換為Integer類型,顯然是不合理的
@Test public void test1() { List list=new ArrayList(); list.add("123"); list.add("123"); list.add(8848); list.add(111); list.add(100); list.remove(new Integer(100)); System.out.println(list); }
[123, 123, 8848, 111]
直接傳遞引用類型的數據,不會再調用參數列表為int類型的remove方法,而是直接調用按照內容移除元素的方法
總結:
如果元素的類型為int的,默認按照索引刪除;如果想要按照指定的元素刪除,則需要裝箱後再刪除
(3)修改
(1)修改指定位置的元素:
@Test public void test1() { List list=new ArrayList(); list.add("123"); list.add("123"); list.add(8848); list.add(111); list.add(100); list.set(1,"2020年8月5日08:41:50"); System.out.println(list); }
[123, 2020年8月5日08:41:50, 8848, 111, 100]
(4)查找
@Test public void test1() { List list=new ArrayList(); list.add("123"); list.add("123"); list.add(8848); list.add(111); list.add(100); System.out.println(list.indexOf(100)); }
4
3、list介面的遍歷方式
(1)迭代器
@Test public void test1() { List list=new ArrayList(); list.add("123"); list.add("123"); list.add(8848); list.add(111); list.add(100); Iterator iterator=list.iterator(); while (iterator.hasNext()){ Object l=iterator.next(); System.out.println(l); } }
123 123 8848 111 100
(2)增強for
@Test public void test1() { List list=new ArrayList(); list.add("123"); list.add("123"); list.add(8848); list.add(111); list.add(100); for (Object l:list){ System.out.println(l); } }
123 123 8848 111 100
(3)普通for
@Test public void test1() { List list=new ArrayList(); list.add("123"); list.add("123"); list.add(8848); list.add(111); list.add(100); for (int i=0;i<list.size();i++){ System.out.println(list.get(i)); } }
123 123 8848 111 100
因為list介面可以操作索引,因此,可以採用for循環遍歷list介面的數據
4、ArrayList源碼分析與底層結構
(1)可變長度的實現
程式碼:
@Test public void test1() { ArrayList list=new ArrayList(); for (int i=0;i<=10;i++){ list.add("join"+i); } for (int i=11;i<=20;i++){ list.add("join"+i); } System.out.println(list); }
運行至集合中有10個元素:
底層實際上是可變數組,該數組是Object類型的,初始的時候數組的長度為0,第一次擴容為10,第二次為15,第三次增加為22… … 也就是說數組的長度以1.5倍的速度增加
源碼分析:
因為該變數是一個長度為0的數組:
add方法:
數組的擴容:
elementData是存儲集合的可變數組:
5、Vector底層與ArrayList的對比
(1)相同點
底層都是可變數組
(2)不同點
版本:
- ArrayList:新
- Vector:舊
執行緒安全:
- ArrayList:執行緒不安全,效率高
- Vector:執行緒安全(同步),效率低
擴容倍數:
- Vector變為原來的2倍
- Arraylist:變為原來的1.5倍
6、LinkedList底層和源碼分析
(1)LinkedList
底層是雙向鏈表
- 每個結點維護三個屬性:item代表自己,next代表下一個元素,prev代表前一個元素
- LinkedList維護兩個重要屬性:first和last,分別指向首結點和尾結點
(2)源碼分析
構造器為空:
public LinkedList() { }
添加元素的源碼分析:
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) first = newNode; else l.next = newNode; size++; modCount++; }
new Node<>(l, e, null):三個參數分別是:前一個、當前元素、下一個
尾插法添加元素,第一次添加first就是第一個元素,不為空的時候在last後添加
(3)與ArrayList的對比
底層結構
ArrayList:可變數組
LinkedList:雙向鏈表
增刪的效率
ArrayList:需要移動大量的元素效率較低
LinkedList:插入時不需要移動記憶體中的元素,只需要改變引用的指向即可,所以插入或刪除的效率高
改查的效率
ArrayList:具有索引,查詢或修改快
LinkedList:查詢時從頭部開始,一個一個地找,查詢效率較低
使用場景
ArrayList:使用在查詢比較多,插入刪除較少的情況
LinkedList:使用在查詢比較少,插入和刪除比較多的情況
總結:
1、List的介面的三個實現類的選擇
如果考慮執行緒安全的問題,採用Vector
不考慮執行緒安全的問題的話,如果查詢較多用ArrayList,增刪較多用LinkedList