Java?數(shù)據(jù)結(jié)構(gòu)深入理解ArrayList與順序表
一、ArrayList簡介
在集合框架中,ArrayList是一個普通的類,實(shí)現(xiàn)了List接口,具體框架圖如下:

ArrayList底層是一段連續(xù)的空間,并且可以動態(tài)擴(kuò)容,是一個動態(tài)類型的順序表。
二、ArrayList的使用
1、ArrayList的構(gòu)造
| 方法 | 使用 |
|---|---|
| ArrayList() | 無參構(gòu)造 |
| ArrayList(Collection<? extends E> c) | 利用其他 Collection 構(gòu)建 ArrayList |
| ArrayList(int initialCapacity) | 指定順序表初始容量 |
public static void main(String[] args) {
// 無參構(gòu)造
List<Integer> list1 = new ArrayList<>();
// 給定初始容量
List<Integer> list2 = new ArrayList<>(10);
// 使用另外一個 ArrayList對其初始化
List<Integer> list3 = new ArrayList<>(list2);
list1.add(1);
list1.add(2);
list1.add(3);
// 其父類 AbstractCollection重寫了 toString方法
System.out.println(list1);// 輸出 [1, 2, 3]
}
2、ArrayList的遍歷
1、遍歷順序表
2、for - each(實(shí)現(xiàn)了Iterable接口)
3、迭代器(實(shí)現(xiàn)了Iterable接口)

// 遍歷順序表
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i));
}
// for - each 遍歷
for (String s : list) {
System.out.print(s);
}
// 迭代器打印
// 獲取迭代器對象
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
// 獲取下一個對象
String next = iterator.next();
// 打印
System.out.print(next);
}
// listIterator ---- 實(shí)現(xiàn)了 Iterator 接口
ListIterator<String> iterator2 = list.listIterator();
while (iterator2.hasNext()) {
String next = iterator2.next();
System.out.print(next);
}
這里的 listIterator 實(shí)現(xiàn)了 Iterator 接口,從方法上,listIterator 有更多的功能(方法),例如在遍歷的時候,進(jìn)行添加元素 add()。
ListIterator<String> iterator2 = list.listIterator();
while (iterator2.hasNext()) {
String next = iterator2.next();
if (next.equals("hello")) {
iterator2.add("三團(tuán)");// 在 hello 的后面添加 三團(tuán)
}else{
System.out.print(next + " ");
}
}
System.out.println(list);// [hello, 三團(tuán), bit, world]
3、ArrayList的常見操作(方法)
| 方法 | 解釋 |
|---|---|
| boolean add(E e) | 尾插 e |
| void add(int index, E element) | 將 e 插入到 index 位置 |
| boolean addAll(Collection<? extends E> c) | 將集合 c 中的元素 尾插到該集合中 |
| E remove(int index) | 刪除 index 位置元素并返回 |
| boolean remove(Object o) | 刪除遇到的第一個 o |
| E get(int index) | 獲取下標(biāo) index 位置元素 |
| E set(int index, E element) | 將下標(biāo) index 位置元素設(shè)置為 element |
| void clear() | 清空順序表 |
| boolean contains(Object o) | 判斷 o 是否在線性表中 |
| int indexOf(Object o) | 返回第一個 o 所在下標(biāo) |
| int lastIndexOf(Object o) | 返回最后一個 o 的下標(biāo) |
| List< E > subList(int fromIndex, int toIndex) | 截取部分 list |
List<String> list = new ArrayList<>();
List<String> listAdd = new ArrayList<>();
listAdd.add("hello");
listAdd.add("world");
listAdd.add("你好~");
list.add("哈哈");// 尾插元素
list.add(0,"你好"); // 0 下標(biāo)插入 "你好 "
list.addAll(listAdd);// 將集合 listAdd 中的元素尾插到該集合中
String s = list.remove(0);// 刪除 index 位置元素并返回
boolean s2 = list.remove("hello");// 刪除遇到的第一個 hello,沒找到則返回 false
list.set(0,"we");
list.indexOf("we");//返回第一個 "we" 所在下標(biāo)
list.lastIndexOf("we");// 返回最后一個 "we" 的下標(biāo)
System.out.println(list);
// 截取子串 -- 左閉右開區(qū)間
List<String> sub = list.subList(1, 3);
System.out.println(sub);
list.set(2,"修改后的list");
System.out.println(sub);
注意: 這里的 subList方法,并不是真正的返回一個截取部分的新地址,而是將原地址的截取部分返回,所以當(dāng)修改原來的線性表中的元素時,子串中的內(nèi)容也會發(fā)生改變。

4、ArrayList的擴(kuò)容機(jī)制
1、當(dāng)調(diào)用無參構(gòu)造時,即List< String > list = new ArrayList<>(),底層還沒有分配真正的內(nèi)存(初始化是一個空數(shù)組),初始容量為 0。當(dāng)?shù)谝淮翁砑釉兀ㄕ{(diào)用 add 方法) 時,整個順序表的容量被擴(kuò)充為10,放滿后,以 1.5 倍擴(kuò)容。


2、當(dāng)調(diào)用帶容量的構(gòu)造方法時,例如 List< String > list = new ArrayList<>(16),順序表初始容量就為16,放滿后以 1.5 倍擴(kuò)容。

結(jié)論
如果調(diào)用無參構(gòu)造方法,順序表初始大小為0,當(dāng)?shù)谝淮畏湃朐貢r,整個順序表容量變?yōu)?0,當(dāng)放滿10個元素,進(jìn)行1.5倍擴(kuò)容。
如果調(diào)用給定容量的構(gòu)造方法,初始大小就是給定的容量,當(dāng)放滿了,就進(jìn)行1.5倍擴(kuò)容。
三、模擬實(shí)現(xiàn)一個順序表(Object[])
public class MyArrayList<E> {
private Object[] elementData;// 數(shù)組
private int usedSize;// 代表有效的數(shù)據(jù)個數(shù)
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public MyArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public MyArrayList(int capacity) {
// 判斷參數(shù)的合法性
if (capacity >= 0) {
this.elementData = new Object[capacity];
}else {
throw new RuntimeException("初始化的容量不能為負(fù)數(shù)!");
}
}
/**
* 添加元素
* @param e 要添加的元素
*/
public boolean add(E e) {
// 確定一個真正的容量,預(yù)測 -> 如需擴(kuò)容則擴(kuò)容
ensureCapacityInternal(usedSize + 1);
// 擴(kuò)容完畢,放數(shù)據(jù)
elementData[usedSize++] = e;
return true;
}
/**
* 給 index位置添加元素
* @param index
* @param e
*/
public boolean add(int index, E e) {
// 檢查 index 是否合法
rangeCheckForAdd(index);
// 確定一個真正的容量 -> 如需擴(kuò)容則擴(kuò)容
ensureExplicitCapacity(usedSize + 1);
// 移動 index后面的元素,并在 index位置插入元素
copy(index,e);
usedSize++;
return true;
}
private void copy(int index, E e){
for (int i = usedSize; i > index; i--) {
elementData[i] = elementData[i - 1];
}
elementData[index] = e;
}
private void rangeCheckForAdd(int index) {
if (index > usedSize || index < 0)
throw new IndexOutOfBoundsException("index位置不合法!");
}
public void ensureCapacityInternal(int minCapacity) {
// 計算出需要的容量
int capacity = calculateCapacity(elementData, minCapacity);
// 根據(jù)計算出的容量,看是否需要擴(kuò)容或者分配內(nèi)存
ensureExplicitCapacity(capacity);
}
private void ensureExplicitCapacity(int minCapacity) {
// 如果需要的容量大于數(shù)組容量,就擴(kuò)容
if (minCapacity - elementData.length > 0)
// 擴(kuò)容
grow(minCapacity);
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
// 說明你要的容量非常大,就分配更大的內(nèi)存
newCapacity = hugeCapacity(minCapacity);;
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 確定之前數(shù)組是否分配過內(nèi)存,沒有的話返回一個初始化的容量 10
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(10, minCapacity);
}
// 分配后,返回 +1 后的值,即實(shí)際所需要的容量
return minCapacity;
}
}
到此這篇關(guān)于Java 數(shù)據(jù)結(jié)構(gòu)深入理解ArrayList與順序表的文章就介紹到這了,更多相關(guān)Java ArrayList內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java請求調(diào)用參數(shù)格式為form-data類型的接口代碼示例
這篇文章主要給大家介紹了關(guān)于Java請求調(diào)用參數(shù)格式為form-data類型的接口的相關(guān)資料,文中給出了詳細(xì)的代碼示例,對大家的學(xué)習(xí)或者工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-08-08
SpringBoot3.x集成nacos并實(shí)現(xiàn)多環(huán)境配置的操作步驟
本文詳細(xì)介紹了如何在Springboot3.x中集成Nacos2.x版本,包括nacos的安裝、配置更改,以及在集成過程中遇到的問題,如端口設(shè)置、依賴版本調(diào)整等,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下2024-10-10
Java面試題沖刺第二十六天--實(shí)戰(zhàn)編程
這篇文章主要為大家分享了最有價值的三道java實(shí)戰(zhàn)面試題,涵蓋內(nèi)容全面,包括數(shù)據(jù)結(jié)構(gòu)和算法相關(guān)的題目、經(jīng)典面試編程題等,感興趣的小伙伴們可以參考一下2021-08-08
如何自定義springboot-starter日志組件供各個服務(wù)使用(系統(tǒng)日志優(yōu)化)
文章介紹了如何將各個微服務(wù)的接口調(diào)用日志邏輯優(yōu)化為一個可共享的Spring Boot Starter,通過自定義注解和自動裝配機(jī)制實(shí)現(xiàn),本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2025-01-01
Java實(shí)現(xiàn)獲取行政區(qū)劃的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用Java語言實(shí)現(xiàn)獲取行政區(qū)劃的功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)游戲2023-03-03
獲取Java的MyBatis框架項(xiàng)目中的SqlSession的方法
SqlSession中包括已經(jīng)映射好的SQL語句,這樣對象實(shí)例就可以直接拿過來用了,那么這里就來講解獲取Java的MyBatis框架項(xiàng)目中的SqlSession的方法2016-06-06
Java基于高精度整型實(shí)現(xiàn)fibonacci數(shù)列的方法
這篇文章主要介紹了Java基于高精度整型實(shí)現(xiàn)fibonacci數(shù)列的方法,是比較典型的算法,需要的朋友可以參考下2014-09-09

