詳解Java ArrayList

来源:https://www.cnblogs.com/KRDecad3/archive/2023/11/01/17800902.html
-Advertisement-
Play Games

ArrayList簡介 ArrayList是List介面的實現類,底層基於數組實現,容量可根據需要動態增加,相當於動態數組。ArrayList繼承於AbstractList,並且還實現了Cloneable、Serializable、RandomAccess介面。 List:表明是列表數據結構,可以通 ...


ArrayList簡介

ArrayList是List介面的實現類,底層基於數組實現,容量可根據需要動態增加,相當於動態數組。ArrayList繼承於AbstractList,並且還實現了CloneableSerializableRandomAccess介面。
image

  • List:表明是列表數據結構,可以通過下標對元素進行添加刪除或查找。
  • Serializable:表示可以進行序列化和反序列化操作,可以把對象與位元組流相互轉換。
  • RandomAccess:有這個介面標記的List表示可以支持快速隨機訪問,即通過元素下標可以直接得到元素內容。
  • Cloneable:表示支持拷貝,可以通過淺拷貝或深拷貝來複制對象的副本。

常用方法

public class ArrayListTest {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(1); //添加元素
        arrayList.add(2);
        arrayList.add(3);
        arrayList.add(4);
        //在指定下標處插入元素
        arrayList.add(4, 5);
        //獲取ArrayList大小
        System.out.println(arrayList.size()); //輸出5
        //獲取指定下標的元素
        System.out.println(arrayList.get(4)); //輸出5
        //移除指定下標的元素
        arrayList.remove(0);
        //判斷ArrayList內是否有指定元素
        System.out.println(arrayList.contains(5)); //輸出true
        //修改指定下標的元素值
        arrayList.set(0, 9);
        System.out.println(arrayList.get(1)); //輸出3
    }
}

ArrayList核心源碼分析

以下基於JDK17的ArrayList代碼。

類屬性

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
	//序列號版本號
    @java.io.Serial
    private static final long serialVersionUID = 8683452581122892189L;

	//預設初始容量
    private static final int DEFAULT_CAPACITY = 10;

	//空數組,指定預設初始化容量為0時賦值給elementData,避免了重覆創建空數組
    private static final Object[] EMPTY_ELEMENTDATA = {};

	//當調用無參構造方法時,賦值給elementData,主要用於在添加第一個元素前,標記該ArrayList是由無參構造器創建,便於將容量初始化為DEFAULT_CAPACITY,避免了重覆創建空數組
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    //實際存放元素的數組
    transient Object[] elementData; // non-private to simplify nested class access

    //ArrayList的元素個數
    private int size;
}

構造方法

//無參構造器,最開始創建的是空數組,當添加第一個元素時初始化容量為10。
public ArrayList() {
	this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

//指定初始化容量,為0的話則創建空數組。
public ArrayList(int initialCapacity) {
	if (initialCapacity > 0) {
		this.elementData = new Object[initialCapacity];
	} else if (initialCapacity == 0) {
		this.elementData = EMPTY_ELEMENTDATA;
	} else {
		throw new IllegalArgumentException("Illegal Capacity: "+
										   initialCapacity);
	}
}

//傳入一個集合,將該集合中的元素存到ArrayList中。
public ArrayList(Collection<? extends E> c) {
	Object[] a = c.toArray();
	if ((size = a.length) != 0) {
		if (c.getClass() == ArrayList.class) {
			elementData = a;
		} else {
			elementData = Arrays.copyOf(a, size, Object[].class);
		}
	} else {
		// replace with empty array.
		elementData = EMPTY_ELEMENTDATA;
	}
}

add

ArrayList對外提供了兩個add方法,add(E e)add(int index, E element),前者是直接在ArrayList尾部追加元素,後者是在指定的位置添加元素。

public boolean add(E e) {
	//記錄結構上被修改的次數
	modCount++;
	add(e, elementData, size);
	return true;
}

private void add(E e, Object[] elementData, int s) {
	//如果當前數組長度等於ArrayList容量則擴容
	if (s == elementData.length)
		elementData = grow();
	elementData[s] = e;
	size = s + 1;
}

public void add(int index, E element) {
	//檢查下標是否越界
	rangeCheckForAdd(index);
	modCount++;
	final int s;
	Object[] elementData;
	//如果當前數組長度等於ArrayList容量則擴容
	if ((s = size) == (elementData = this.elementData).length)
		elementData = grow();
	//把index位置及其後的元素向後移動一位
	System.arraycopy(elementData, index,
					 elementData, index + 1,
					 s - index);
	elementData[index] = element;
	size = s + 1;
}

get

get方法比較簡單,首先檢查下標是否越界,然後找到數組中指定下標的元素。

public E get(int index) {
	Objects.checkIndex(index, size);
	return elementData(index);
}

remove

ArrayList也對外提供了兩個remove方法public E remove(int index)public boolean remove(Object o),前者根據指定下標刪除元素,後者傳入一個對象,將ArrayList中的這個對象刪除,都是通過調用fastRemove方法進行實際刪除元素的。

public E remove(int index) {
	Objects.checkIndex(index, size);
	final Object[] es = elementData;

	@SuppressWarnings("unchecked") E oldValue = (E) es[index];
	fastRemove(es, index);

	return oldValue;
}

public boolean remove(Object o) {
	final Object[] es = elementData;
	final int size = this.size;
	int i = 0;
	//遍曆數組是否有該對象並找到對應下標
	found: {
		if (o == null) {
			for (; i < size; i++)
				if (es[i] == null)
					break found;
		} else {
			for (; i < size; i++)
				if (o.equals(es[i]))
					break found;
		}
		return false;
	}
	fastRemove(es, i);
	return true;
}

fastRemove方法:

private void fastRemove(Object[] es, int i) {
	modCount++;
	final int newSize;
	if ((newSize = size - 1) > i)
		//把i+1之後的元素往前移動一位
		System.arraycopy(es, i + 1, es, i, newSize - i);
	//size-1位置置為空,對象最後會被gc
	es[size = newSize] = null;
}

擴容

ArrayList擴容是通過調用grow方法,在add方法和ensureCapacity都有出現。

ensureCapacity方法,傳入一個最小容量,如果最小容量大於實際容量則擴容到最小容量。

public void ensureCapacity(int minCapacity) {
	if (minCapacity > elementData.length
		&& !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
			 && minCapacity <= DEFAULT_CAPACITY)) {
		modCount++;
		grow(minCapacity);
	}
}

下麵看下grow方法:

private Object[] grow(int minCapacity) {
	int oldCapacity = elementData.length;
	if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
		//獲取新容量大小
		//oldCapacity右移一位即是原長度的一半,ArraysSupport.newLength方法選擇minCapacity - oldCapacity或oldCapacity >> 1較大的一方與oldCapacity相加。
		int newCapacity = ArraysSupport.newLength(oldCapacity,
												  minCapacity - oldCapacity, /* minimum growth */
												  oldCapacity >> 1           /* preferred growth */);
		//將之前元素遷移到新數組,返回按照新容量擴容後的數組
		return elementData = Arrays.copyOf(elementData, newCapacity);
	} else {
		//如果容量為0,則按照預設容量創建一個數組。
		return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
	}
}

private Object[] grow() {
	return grow(size + 1);
}

參考資料

  1. ArrayList 源碼分析
  2. 【Java容器(jdk17)】ArrayList深入源碼,就是這麼簡單

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 創建名為spring_mvc_ajax的新module,過程參考9.1節和9.5節 10.1、SpringMVC處理Ajax請求 10.1.1、頁面請求示例 <input type="button" value="測試SpringMVC處理Ajax請求" onclick="testAjax()"> ...
  • 一、 前言 ​ 最近在看tomcat connector組件的相關源碼,對Nio2的非同步回調過程頗有興趣,平時讀源碼不讀,自己讀的時候很多流程都沒搞明白,去查網上相關解析講的給我感覺也不是特別清晰,於是就自己慢慢看源碼,以下是我自己的見解,因為開發經驗也不多,剛成為社畜不久,有些地方講錯如果有大佬看 ...
  • 在如今這個信息爆炸的時代,短視頻成為了一種非常受歡迎的娛樂方式。而在短視頻中,各種搞笑的內容更是大受歡迎。因此,開發一個能夠讓人們笑翻天的笑話短視頻介面就成為了一個非常有趣的項目。本文將介紹如何使用挖數據平臺的API來開發一個簡單的笑話短視頻介面,並提供代碼說明。 API介紹 挖數據平臺提供了一個非 ...
  • 中斷系統:是執行和管理中斷的邏輯結構 外部中斷:是眾多能產生中斷的外設之一 中斷:指的是中斷源(中斷通道),中斷產生CPU暫停正在執行程式,去執行中斷程式,然後返回。提高效率 F1系列的STM32有68個中斷源,不同系列需要看手冊 EXTI(外部中斷)、TIM、ADC、USART、SPI、I2C、R ...
  • 同系列文章 QT中級(1)QTableView自定義委托(一)實現QSpinBox、QDoubleSpinBox委托 QT中級(2)QTableView自定義委托(二)實現QProgressBar委托 QT中級(3)QTableView自定義委托(三)實現QCheckBox委托並且將QCheckBo ...
  • 背景 現代網路環境中,敏感數據的處理是至關重要的。敏感數據包括個人身份信息、銀行賬號、手機號碼等,泄露這些數據可能導致用戶隱私泄露、財產損失等嚴重後果。因此,對敏感數據進行脫敏處理是一種必要的安全措施。 比如頁面上常見的敏感數據都是加*遮擋處理過的,如下圖所示。 接下來本文將以Spring Boot ...
  • 來源:blog.csdn.net/mu_wind/article/details/113806680 初識線程池 我們知道,線程的創建和銷毀都需要映射到操作系統,因此其代價是比較高昂的。出於避免頻繁創建、銷毀線程以及方便線程管理的需要,線程池應運而生。 線程池優勢 降低資源消耗:線程池通常會維護一些 ...
  • wmproxy wmproxy已用Rust實現http/https代理, socks5代理, 反向代理, 靜態文件伺服器,四層TCP/UDP轉發,內網穿透,後續將實現websocket代理等,會將實現過程分享出來,感興趣的可以一起造個輪子 項目地址 國內: https://gitee.com/tic ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...