摘要:部分源碼分析小記底層數據結構底層的數據結構就是數組,數組元素類型為類型,即可以存放所有類型數據。初始容量大于初始化元素數組新建一個數組初始容量為為空對象數組初始容量小于,拋出異常無參構造函數。
JDK1.8 ArrayList部分源碼分析小記 底層數據結構
底層的數據結構就是數組,數組元素類型為Object類型,即可以存放所有類型數據。
我們對ArrayList類的實例的所有的操作底層都是基于數組的。
ArrayList繼承的父類為:AbstractList(抽象類)
實現的接口有:List(規定了List的操作規范)、RandomAccess(可隨機訪問)、Cloneable(可拷貝)、Serializable(可序列化)
友情提示:因為ArrayList實現了RandomAccess接口,所以盡量用for(int i = 0; i < size; i++) 來遍歷而不要用Iterator迭代器來遍歷,后者在效率上要差一些
public class ArrayList部分屬性extends AbstractList implements List , RandomAccess, Cloneable, java.io.Serializable
類的屬性中核心的屬性為elementData,類型為Object[],用于存放實際元素,并且被標記為transient,也就意味著在序列化的時候,此字段是不會被序列化的。
友情提示:ArrayList的默認容量為10
// 缺省容量 private static final int DEFAULT_CAPACITY = 10; // 元素數組(調用指定初始值的構造函數時elementData的長度會變成指定值) transient Object[] elementData; // 空對象數組 private static final Object[] EMPTY_ELEMENTDATA = {}; // 缺省空對象數組 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};常用構造函數
友情提示:創建ArrayList時盡量設置初始大小(使用ArrayList(int initialCapacity)構造函數)
/** * ArrayList帶容量大小的構造函數 * * @param initialCapacity */ //說明:指定elementData數組的大小,不允許初始化大小小于0,否則拋出異常。 public ArrayList(int initialCapacity) { if (initialCapacity > 0) {// 初始容量大于0 this.elementData = new Object[initialCapacity];// 初始化元素數組(新建一個數組) } else if (initialCapacity == 0) {// 初始容量為0 this.elementData = EMPTY_ELEMENTDATA;// 為空對象數組 } else {// 初始容量小于0,拋出異常 throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity); } } /** * ArrayList無參構造函數。默認容量是10 */ //說明:當未指定初始化大小時,會給elementData賦值為空集合。 public ArrayList() { // 無參構造函數,設置元素數組為空 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }常用函數分析
友情提示:
add方法:當容量到達size時進行擴容(add(E e)中先調用了ensureCapacity(size+1)方法,之后將元素的索引賦給elementData[size],而后size自增),擴容為當前容量的0.5倍(若果ArrayList的當前容量為10,那么一次擴容后的容量為15)
set方法:調用set方法時會檢驗索引是否合法(只校驗了上限)(index不能等于size(index
/** * 添加元素 * * @param e * @return */ public boolean add(E e) { //確保elementData數組有合適的大小 ensureCapacityInternal(size + 1); elementData[size++] = e; return true; } /** * 設定指定下標索引的元素值 * * @param index * @param element * @return */ public E set(int index, E element) { // 檢驗索引是否合法 rangeCheck(index); // 舊值 E oldValue = elementData(index); // 賦新值 elementData[index] = element; // 返回舊值 return oldValue; } /** * 獲取指定下標的元素 * * @param index * @return */ //說明:get函數會檢查索引值是否合法(只檢查是否大于size,而沒有檢查是否小于0), // 值得注意的是,在get函數中存在element函數,element函數用于返回具體的元素 public E get(int index) { // 檢驗索引是否合法 rangeCheck(index); return elementData(index); } // Positional Access Operations /** * 位置訪問操作 * * @param index * @return */ //說明:返回的值都經過了向下轉型(Object -> E(泛型)) @SuppressWarnings("unchecked") E elementData(int index) { return (E) elementData[index]; } /** * 移除指定下標元素 * * @param index * @return */ //說明:remove函數用戶移除指定下標的元素 // 此時會把指定下標到數組末尾的元素向前移動一個單位,并且會把數組最后一個元素設置為null,這樣是為了方便之后將整個數組不被使用時,可以會被GC。 //提醒:元素移動時使用的是System.arraycopy()方法 public E remove(int index) { // 檢查索引是否合法 rangeCheck(index); modCount++; E oldValue = elementData(index); // 需要移動的元素的個數 int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index + 1, elementData, index, numMoved); // 賦值為空,有利于進行GC elementData[--size] = null; // clear to let GC do its work // 返回舊值 return oldValue; } /** * 在指定下標位置插入元素 * @param index * @param element */ public void add(int index, E element) { //檢查索引是否合法 rangeCheckForAdd(index); ensureCapacityInternal(size + 1); System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; }其他方法介紹
友情提示:rangeCheckForAdd方法用于add(int index, E element)和addAll(int index, Collection extends E> c)方法中檢驗索引是否合法;rangeCheck方法用于get、set等方法中檢驗索引是否合法(因為不改變數據結構,故index不能取到size,最大只能取到size-1)
//檢驗索引是否合法(只校驗了上限)(index不能等于size(index擴容策略= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } //檢驗索引是否合法(校驗了上限和下限)(index可以等于size(0<=index<=size)) private void rangeCheckForAdd(int index) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }
// 確定ArrarList的容量。 // 若ArrayList的容量不足以容納當前的全部元素,設置 新的容量=“(原始容量x3)/2 + 1” public void ensureCapacity(int minCapacity) { int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ? 0 : DEFAULT_CAPACITY;//默認容量10 if (minCapacity > minExpand) { ensureExplicitCapacity(minCapacity); } } //確保elementData數組有合適的大小 private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {// 判斷元素數組是否為空數組 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);// 取較大值 } //確保elemenData數組有合適的大小 ensureExplicitCapacity(minCapacity); } //確保elemenData數組有合適的大小 private void ensureExplicitCapacity(int minCapacity) { // 結構性修改加1 modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } //對數組進行擴容 private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length;// 舊容量 int newCapacity = oldCapacity + (oldCapacity >> 1);// 新容量為舊容量的1.5倍 if (newCapacity - minCapacity < 0)// 新容量小于參數指定容量,修改新容量 newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0)// 新容量大于最大容量 newCapacity = hugeCapacity(minCapacity);// 指定新容量 // 拷貝擴容 elementData = Arrays.copyOf(elementData, newCapacity); }部分方法測試
add方法
public class AddTest { @Test public void test1(){ //我們可以看到,在add方法之前開始elementData = {}; // 調用add方法時會繼續調用,直至grow,最后elementData的大小變為10, // 之后再返回到add函數,把8放在elementData[0]中 Listlists = new ArrayList (); lists.add(8); } @Test public void test2(){ //說明:我們可以知道,在調用add方法之前,elementData的大小已經為6,之后再進行傳遞,不會進行擴容處理。 List lists = new ArrayList (6);//elementData.length=6 lists.add(8); } }
rangeCheck方法
public class RangeCheckTest { @Test public void test() { List list = new ArrayList(); list.add(1); list.add(2); list.add(3); //該語句報ArrayIndexOutOfBoundsException異常是rangeCheck(index)引發的(index >= size) System.out.println(list.get(10)); //rangeCheck(index)方法只校驗上線,該語句報ArrayIndexOutOfBoundsException異常是elementData[index]引發的 System.out.println(list.get(-1)); list.remove(-1);//同上 Object[] a = new Object[]{1, 2, 3}; System.out.println(a[-1]); } }
indexOf方法
public class IndexOfTest { @Test public void test(){ List list = new ArrayList(); list.add(null); list.add(2); list.add(2); list.add(null); System.out.println(list.indexOf(null));//0 System.out.println(list.indexOf(2));//1 System.out.println(list.indexOf(3));//-1 } }
toArray方法
public class ToArrayTest { @Test public void test() { List list = new ArrayList(10); list.add(1); list.add(2); list.add(3); list.add(4); Object[] array =list.toArray(); //調用Arrays.copyOf()-->調用System.arraycopy() } }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/67625.html
摘要:若遇到哈希沖突,則將沖突的值加到鏈表中即可。之后相比于之前的版本,之后在解決哈希沖突時有了較大的變化,當鏈表長度大于閾值默認為時,將鏈表轉化為紅黑樹,以減少搜索時間。有序,唯一紅黑樹自平衡的排序二叉樹。 本文是最最最常見Java面試題總結系列第三周的文章。主要內容: Arraylist 與 LinkedList 異同 ArrayList 與 Vector 區別 HashMap的底層...
摘要:三系列用于保存鍵值對,無論是,還是已棄用的或者線程安全的等,都是基于紅黑樹。是完全基于紅黑樹的,并在此基礎上實現了接口。可以看到,只有紅黑樹,且紅黑樹是通過內部類來實現的。 JDK容器 前言 閱讀JDK源碼有段時間了,準備以博客的形式記錄下來,也方便復習時查閱,本文參考JDK1.8源碼。 一、Collection Collection是所有容器的基類,定義了一些基礎方法。List、Se...
摘要:對于不同的實現,對象占用的內存空間大小可能不盡相同,本文主要分析中的情況,實驗環境為位系統,使用進行結論驗證。內存占用這里分析一個只有一組鍵值對的結構如下首先分析本身的大小。 本文深入分析并驗證了不同Java對象占用內存空間大小的情況。對于不同的jvm實現,Java對象占用的內存空間大小可能不盡相同,本文主要分析HotSpot jvm中的情況,實驗環境為64位window10系統、JD...
摘要:需要注意的是,通過構造函數定義初始量是動態數組的實際大小。帶容量的構造函數新建一個容量為的數組默認構造函數,默認為空構造一個包含指定元素的第一個構造方法使用提供的來初始化數組的大小。 前言 今天介紹經常使用的一個Java集合類——ArrayList(基于JDK1.8.0_121)。ArrayList在工作和日常面試中經常被使用或者提到。總的來說,工作中使用ArrayList主要是因為動...
閱讀 2488·2021-08-11 11:16
閱讀 2926·2019-08-30 15:55
閱讀 3332·2019-08-30 12:53
閱讀 1568·2019-08-29 13:28
閱讀 3264·2019-08-28 18:17
閱讀 936·2019-08-26 12:19
閱讀 2466·2019-08-23 18:27
閱讀 696·2019-08-23 18:17