Java中ArrayList
- 一 ArrayList的簡介
- 二 ArrayList的構造方法
- 三 ArrayList常用方法
- 1.add()方法
- 2.remove()方法
- 3.get()和set()方法
- 4.index()方法
- 5.subList截取方法
- 四 ArrayList的遍歷
- for循環遍歷
- 增強for循環(for each)
- 迭代器遍歷
- ArrayList問題及其思考
前言
ArrayList是一種 順序表,??段 物理地址連續的存儲單元依次存儲數據的線性結構,一般情況都是采用數組存儲,在數組上實現一些增刪查改等等功能, ArrayList是Java中最常用的 動態數組實現之一,其內部的內存空間是可以改變的,今天就來探究一下ArrayList使用及其原理
一 ArrayList的簡介
ArrayList雖然是一個普通的類,但是它實現了List接口
List接口在JDK17的部分源碼
ArrayList類在JDK17的部分源碼
我們可以從上面看出
1. List是一個接口,繼承了Collection類
2. ArrayList是一個類,繼承了AbstractList類
3. ArrayList實現了List中的RandomAccess接口,說明其支持隨機訪問
4. 實現了Cloneable接口,說明其支持clone
5. 實現了Serializable接口,說明其支持序列化
下面是ArrayList的一些常用方法,因為其實現了List接口,所以List中的抽象方法,在ArrayList都有
二 ArrayList的構造方法
ArrayList () | 無參構造 |
---|---|
ArrayList (int initialCapacity) | 確定初始容量 |
ArrayList (Collection<? extends E> c) | 利用其他Collection來構造 |
public class Test {public static void main(String[] args) {//1.無參構造List<Integer> list = new ArrayList<>();//2.含參構造,確定其初始化的容量,確定其初始化容量為1List<Integer> list1 = new ArrayList<>(1);//3.利用其他構造//這里list2與list的元素一致List<Integer> list2 = new ArrayList<>(list);}
}
無參構造源碼
雖然無參構造沒有給定容量,但是編譯器會默認一個容量,JDK17中默認容量為10
含初始容量一個參數的構造
如果給定初始容量,編譯器會進行判斷,如果初始容量>0就按照你給定的容量創建對象,如果初始容量==0就按照默認的容量進行創建,反之如果<0就拋出容量不合法的異常
注意在創建線性表的時候,要指定是什么類型的線性表
1.如果不寫類型,編譯器會報錯,因為其不知道是什么類型
2.并且其必須是包裝類型
如果寫成普通類型編譯器也會報錯
3.那何為包裝類型呢
基本數據類型 | 包裝類 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
這些類型中除了int和char的包裝類有所改變,其他的都是基本類型的首字母大寫
三 ArrayList常用方法
方法 | 解釋 |
---|---|
boolean add(E e) | 尾插 e |
void add(int index, E e) | 將 e插入到 index 位置 |
boolean addAll(Collection<? extends E> c) | 尾插 c 中的全部元素 |
E remove(int index) | 刪除 index 下標元素 |
boolean remove(Object o) | 刪除遇到的第一個 o |
E get(int index) | 獲取下標 index 下標元素 |
E set(int index, E e) | 將下標 index 下標元素設置為 e |
void clear() | 清空列表 |
boolean contains(Object o) | 判斷 o 是否在線性表中 |
int indexOf(Object o) | 返回第一個 o 所在下標 |
int lastIndexOf(Object o) | 返回最后一個 o 的下標 |
List subList(int fromIndex, int toIndex) | 截取部分 list |
int size() | 返回此列表中的實際的元素數 |
1.add()方法
boolean add(E e)
將指定的元素追加到此列表的末尾。
public class Test {public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(1);list.add(2);list.add(3);System.out.println(list);}
}
運行結果如下
注意這里的如果已經確定了是那種包裝類型,就不可以在添加其他的包裝類型的數據了
public class Test {public static void main(String[] args) {List<Integer> list = new ArrayList<>(3);list.add(1);list.add(2);list.add(3);System.out.println("擴容前長度"+list.size());list.add(4);System.out.println("擴容后長度"+list.size());System.out.println(list);}
}
運行結果如下
我們發現一個問題就是上面我們初始化容量為3,但是我們添加了4個元素其并沒有報錯,為什么呢?那是因為其ArrayList類add方法中有擴容操作,但是這里的真實長度變為4
void add(int index, E e)
在index下標插入e
index>=0&&index<=其列表的長度
public class Test {public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(1);list.add(2);list.add(3);//在1下標插入99list.add(1,99);// list.add(4,1);System.out.println(list);}
}
我們要注意這里并不是簡單替換,而是將某一個下標修改為一個數后,原本的數要往后偏移
偏移的同時其如果存儲不下,也會進行擴容
運行結果如下
注意這里的下標要合法 index下標>=0&&index<=list.size()
這里等于其list的容量也可以,因為這樣正好就相當于上面一個參數的add進行尾插
如果大于list.size()就會出現數組越界異常
這里長度為3,這里index>=0&&index<=3,因此這里出現數組越界異常異常了
boolean addAll(Collection<? extends E> c)
將指定集合中的所有元素追加到此列表的末尾
這里是可以自己尾插自己的
public class Test {public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(1);list.add(2);list.add(3);System.out.println("尾插前list"+list);List<Integer> list1 = new ArrayList<>();list1.add(10);list1.add(20);list1.add(30);//將list1尾插到list后面list.addAll(list1);System.out.println("尾插后list"+list);}
}
這里其實也是可以自己尾插自己的
下面這個自己尾插自己成功了
注意這里是不可以不同類型的列表進行尾插
一個列表中不可以放兩種包裝類型一樣,尾插不同類型的肯定會報錯
2.remove()方法
E remove(int index)
刪除 index 下標元素
index>=0&&index<列表長度
public class Test {public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(1);list.add(2);list.add(3);//刪除1下標的值list.remove(1);System.out.println(list);}
}
這里刪除1下標的值,也就是刪除這里的2
運行結果如下
boolean remove(Object o)
刪除遇到的第一個 o
在使用的時候要將一個數裝箱以后再進行刪除
這里直接使用基本類型就是刪除的是對應下標的值,而不是自己想要的
這時候就要進行裝箱操作,這樣就可以刪除對應的值了,刪除的就不是下標對應的值了
這里就是刪除第一個出現的1元素,而不是下標,所以這里0下標的1被刪除了
void clear()
清空列表
public class Test {public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(1);list.add(2);list.add(3);//刪除1下標的值Integer a = 1;list.remove(a);System.out.println(list);list.clear();System.out.println("清空后"+list);}
}
這里就是將其置為空
3.get()和set()方法
E get(int index)
獲取下標 index 下標元素E set(int index, E e)
將下標 index 下標元素設置為 e
index>=0&&index<列表長度
public class Test {public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(1);list.add(2);list.add(3);System.out.println("獲取1下標的值:"+list.get(1));list.set(1,999);System.out.println("修改后1下標的值:"+list.get(1));}
}
這里和add方法不同的是,這個是直接修改值,而add是添加值
運行結果如下
4.index()方法
int indexOf(Object o) 返回第一個 o 所在下標
int lastIndexOf(Object o) 返回最后一個 o 的下標
找不到就返回-1
public class Test {public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(1);list.add(2);list.add(3);list.add(1);System.out.println("第一個元素1的下標:"+list.indexOf(1));//0//如果沒有找到就返回-1System.out.println(list.indexOf(100));//-1System.out.println("最后一個元素1的下標:"+list.lastIndexOf(1));}
}
5.subList截取方法
List subList(int fromIndex, int toIndex)
截取下標[fromIndex,toIndex]的list
public class Test {public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);List<Integer> list1 = list.subList(1,3);//這里是左閉右開的截取方法System.out.println("截取[1,3)下標的元素:"+list1);}
}
運行結果如下
其實這里并不是真正的截取
例如:
public class Test {public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);List<Integer> list1 = list.subList(1,3);//這里是左閉右開的截取方法System.out.println("截取[1,3)下標的元素:"+list1);list1.remove(0);//刪除list1中的0下標的值也就是2System.out.println("被截取的list: "+ list);}
}
這里刪除截取中0下標的2,而原本的list中的2也被刪除了
運行結果如下
可以看出這里我們對截取的內容刪除,導致了list中的這部分內容也被刪除了
只是將其一個對象指向截取的部分,只是將其截取部分的地址放入一個新對象中,因此對截取修改也會影響原本的list
四 ArrayList的遍歷
for循環遍歷
public class Test {public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);for (int i = 0; i < list.size(); i++) {//獲取下標對應的值System.out.print(list.get(i)+" ");}}
}
這里的size()獲取其列表長度
進行一個一個下標遍歷就行
運行結果如下
增強for循環(for each)
public class Test {public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);//每個元素的類型為Integerfor (Integer integer:list) {System.out.print(integer+" ");}}
}
這里使用for-each遍歷,這里列表中元素類型都為Integer,就用一個臨時integer來訪問和打印
迭代器遍歷
public class Test {public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);//使用迭代器//從前向后遍歷Iterator<Integer> it = list.iterator();//獲取其開始位置while(it.hasNext()){//不斷的往后走System.out.print(it.next()+" ");}System.out.println();//從后向前遍歷給定其下標從其最后面開始ListIterator<Integer> it1 = list.listIterator(list.size());while (it1.hasPrevious()){System.out.print(it1.previous()+" ");}System.out.println();}
}
運行結果如下
ArrayList問題及其思考
1.在其查找數據是非常簡單和高效的
2.但是,ArrayList列表在添加或者刪除元素的時候,都需要將其后面的數據向前移動,時間復雜度為O(N)
3.在添加數據時候可能會擴容,是1.5倍數擴容,有點浪費空間
因此還需要其他的數據結構來完成添加或者刪除元素會更高效一點