摘要:說明是對中數組的擴展,其底層還是使用數據實現,支持自動擴容,不是線程安全的類。其繼承,實現了各個接口,其中為支持隨機讀寫的標記接口,在后續類的講解中會用到。加上主要是為了不能直接序列化,而是必須使用自帶的和方法,主要是為了節省空間。
1.說明
ArrayList是對java中數組的擴展,其底層還是使用數據實現,支持自動擴容,不是線程安全的類。其繼承AbstractList,實現了List, RandomAccess, Cloneable,Serializable各個接口,其中RandomAccess為支持隨機讀寫的標記接口,在后續Collections類的講解中會用到。
2.優缺點ArrayList是一個支持自動擴容的線性表,所以其與普通線性表的特點一樣,如下:
順序存儲,所以按照下標讀取元素的時間復雜度為O(1)
在刪除元素時其后面的所有元素都得前移,新增元素時后面的所有元素都得后移
在存儲時就需要開辟一塊固定的存儲空間
3.重要變量//默認的容量,當構造函數沒有傳入此參數時,會在加入第一個元素時將數組擴容為此值 private static final int DEFAULT_CAPACITY = 10; //當數組中元素為空時,會將數組初始化為此數組,代表空數組,這個空數組是在傳入 初始容量并且初始容量為0的情況下令數組等于這個 private static final Object[] EMPTY_ELEMENTDATA = {}; //與上一個變量的區別在于其是在于這個空數組是在使用無參構造函數時創建 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //這個數組用于ArrayList存儲元素,ArrayList的容量就是數組的長度。加上transient主要是為了 不能直接序列化,而是必須使用自帶的writeObject和readObject方法,主要是為了節省空間。不私有 化主要是為了以后擴展,防止以后嵌套等操作 transient Object[] elementData; // non-private to simplify nested class access //ArrayList的實際大小,即元素所占個數 private int size;4.構造方法
//傳入初始參數的構造方法 public ArrayList(int initialCapacity) { //如果初始容量大于0,那么直接按照初始容量初始化數組, 如果初始容量等于0,等于EMPTY_ELEMENTDATA,上文 的常量,不過不會在添加第一個元素的時候令容量等于DEFAULT_CAPACITY 如果初始容量小于0,直接拋出異常 if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } //無參構造函數,直接令存儲數組等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA, 在添加第一個元素的時候令容量等于DEFAULT_CAPACITY public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } //通過一個集合類來構造一個ArrayList public ArrayList(Collection extends E> c) { //獲取集合類的底層數組 elementData = c.toArray(); //判斷集合類的元素個數是否為0 if ((size = elementData.length) != 0) { //ps:c.toArray()可能返回的數組類型不為Object[]類型, 通過ArrayList的toArray一定是Object[]類型,但是Arrays.asList() 里轉化成其內部自身的ArrayList,其內部的ArrayList的toArray方法會 返回E[]這種泛型的對象,導致出現問題 //如果集合類的toArray方法返回的不為Object[]類型,使用Arrays.copyOf 將其遷移到一個新的Object[]數組中 if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // 如果元素個數為0,那么直接將元素數組置為EMPTY_ELEMENTDATA空數組 this.elementData = EMPTY_ELEMENTDATA; } }5.基本方法
//增加一個新元素 public void add(int index, E element) { //檢驗index是否超出范圍 rangeCheckForAdd(index); //檢驗當前容量是否足夠,如果不夠,進行擴容 ensureCapacityInternal(size + 1); //將index之后的元素都后移一個 System.arraycopy(elementData, index, elementData, index + 1, size - index); //將新增的元素填入 elementData[index] = element; //添加數組元素個數 size++; } //檢驗index是否超出范圍 private void rangeCheckForAdd(int index) { //判斷index是否大于當前長度或者小于0,如果不在數組范圍, 那么直接跑出數組超出范圍異常 if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } //確保內部元素的容量足夠 private void ensureCapacityInternal(int minCapacity) { //對數組進行擴容 ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } //用于判斷數組是不是DEFAULTCAPACITY_EMPTY_ELEMENTDATA,此處用==號進行比較,是因為 只用于判斷是構造函數時未傳初始容量,延遲到第一次添加新元素時進行默認初始容量的設置,然后 返回比較默認容量與需要的最小容量的最大值,這里返回最大值是因為有可能一次添加多個元素, AddAll也會復用這個函數 private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } //對數組進行擴容 private void ensureExplicitCapacity(int minCapacity) { //修改次數+1 modCount++; // 如果需要的容量數大于當前容量,進行擴容,否則,不在繼續, 并且防止溢出,如果溢出,不進行下列操作,直接會在后續數組 賦值中直接拋出越界異常,因為這個時候代表size已經很大了 if (minCapacity - elementData.length > 0) grow(minCapacity); } //對數組進行擴容 private void grow(int minCapacity) { int oldCapacity = elementData.length; //令新的容量等于原容量的1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1); //如果新的容量小于當前需要的的最小容量,那么新容量等于當前需要的的最小容量 防止newCapacity溢出或者增加1.5倍還是小于minCapacity的情況 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //如果新的容量比MAX_ARRAY_SIZE還大,MAX_ARRAY_SIZE為 Integer.MAX_VALUE - 8,那么對當前需要的最小容量進行擴容 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); //為數組開辟更大的空間 elementData = Arrays.copyOf(elementData, newCapacity); } //根據需要的容量進行擴容 private static int hugeCapacity(int minCapacity) { //如果minCapacity此時小于0,代表已經溢出,其實此時已經不大可能,因為在 ensureExplicitCapacity已經有一層判斷了 if (minCapacity < 0) throw new OutOfMemoryError(); //如果minCapacity小于MAX_ARRAY_SIZE,那么直接等于MAX_ARRAY_SIZE, 否則直接等于Integer的最大值 return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
下面,來看看Api中的add,addAll等方法,其他類似方法不在贅述,get和set也不贅述,比較簡單
//與add方法不同的是,他是在這個索引處新增了一個集合的元素 public boolean addAll(int index, Collection extends E> c) { //檢驗index是否合法 rangeCheckForAdd(index); Object[] a = c.toArray(); int numNew = a.length; //size + numNew代表需要的最小容量 ensureCapacityInternal(size + numNew); //numMoved代表需要向后移動的元素個數 int numMoved = size - index; if (numMoved > 0) System.arraycopy(elementData, index, elementData, index + numNew, numMoved); //將集合c中的所有元素移動到elementData的index之后 System.arraycopy(a, 0, elementData, index, numNew); size += numNew; //如果集合c的長度不為0,返回true,否則返回false return numNew != 0; } //將elementData中的空元素去除,節省空間(去除不了中間的等于null這樣類型的元素) public void trimToSize() { modCount++; //如果當前元素個數小于elementData的容量,當size == 0時 將elementData賦值為EMPTY_ELEMENTDATA,否則,將elementData 容量減小到size大小 if (size < elementData.length) { elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size); } } //根據對應元素計算出元素位置(遍歷為從前到后) public int indexOf(Object o) { //如果傳入元素為null,找到等于null的元素,否則,使用 equals遍歷 if (o == null) { for (int i = 0; i < size; i++) if (elementData[i]==null) return i; } else { for (int i = 0; i < size; i++) if (o.equals(elementData[i])) return i; } return -1; } //移除當前元素 public E remove(int index) { rangeCheck(index); modCount++; //獲取當前index在數組對應元素 E oldValue = elementData(index); //獲取需要移動的元素個數,即index之后的元素都需要向前移動,不包括index int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); //將最后一個元素置為空,其中的元素有gc自動回收 elementData[--size] = null; //返回被刪除的元素值 return oldValue; } //對對應元素進行移除,先遍歷查找,在進行移除,值得一提的是, 此處的remove方法,使用的是fastRemove,與remove方法具體 少了rangeCheck方法和獲取oldValue,從而減少時間復雜度 public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/77240.html
摘要:數組的大小會根據容量的增長而動態的增長,具體的增長方式請看這里構造函數提供了三種方式的構造器。這些元素按照該的迭代器返回的順序排列的。 原文地址 ArrayList ArrayList是List接口的 可變數組的實現。實現了所有可選列表操作,并允許包括 null 在內的所有元素。除了實現 List接口外,此類還提供一些方法來操作內部用來存儲列表的數組的大小。ArrayList繼承自 A...
摘要:當多個線程對同一個集合的內容進行操作時,就可能會產生事件。當某一個線程遍歷的過程中,的內容被另外一個線程所改變了就會拋出異常,產生事件。在線程在遍歷過程中的某一時刻,線程執行了,并且線程刪除了中的節點。 概要 前面,我們已經學習了ArrayList。接下來,我們以ArrayList為例,對Iterator的fail-fast機制進行了解。 1 fail-fast簡介 fail-fast...
摘要:線程不安全底層數據結構是鏈表。的默認初始化容量是,每次擴容時候增加原先容量的一半,也就是變為原來的倍刪除元素時不會減少容量,若希望減少容量則調用它不是線程安全的。 前言 聲明,本文用得是jdk1.8 前一篇已經講了Collection的總覽:Collection總覽,介紹了一些基礎知識。 現在這篇主要講List集合的三個子類: ArrayList 底層數據結構是數組。線程不安全 ...
摘要:我們來看相關源碼我們看到封裝的和操作其實就是對頭結點的操作。迭代器通過指針,能指向下一個節點,無需做額外的遍歷,速度非常快。不同的遍歷性能差距極大,推薦使用迭代器進行遍歷。LinkedList類介紹 上一篇文章我們介紹了JDK中ArrayList的實現,ArrayList底層結構是一個Object[]數組,通過拷貝,復制等一系列封裝的操作,將數組封裝為一個幾乎是無限的容器。今天我們來介紹JD...
閱讀 986·2021-09-26 10:15
閱讀 2064·2021-09-24 10:37
閱讀 2580·2019-08-30 13:46
閱讀 2631·2019-08-30 11:16
閱讀 2421·2019-08-29 10:56
閱讀 2591·2019-08-26 12:24
閱讀 3472·2019-08-23 18:26
閱讀 2662·2019-08-23 15:43