摘要:要注意的是并不是線程安全的,因此一般建議在單線程中使用。實現(xiàn)原理繼承關(guān)系繼承實現(xiàn)接口關(guān)鍵屬性數(shù)據(jù)的數(shù)組實際數(shù)據(jù)的數(shù)量底層使用數(shù)組保存所有元素如果用聲明一個實例變量,當(dāng)對象存儲時,它的值不需要維持。
概述
ArrayList可以簡單的看作是動態(tài)數(shù)組,相對于普通的數(shù)組它可以動態(tài)的增加容量或者減少容量。要注意的是ArrayList并不是線程安全的,因此一般建議在單線程中使用ArrayList。
實現(xiàn)原理 繼承關(guān)系public class ArrayListextends AbstractList implements List , RandomAccess, Cloneable, java.io.Serializable
ArrayList繼承AbstractList實現(xiàn)List, RandomAccess, Cloneable, java.io.Serializable接口
關(guān)鍵屬性/** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. Any * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA * will be expanded to DEFAULT_CAPACITY when the first element is added. */ // 數(shù)據(jù)的數(shù)組 transient Object[] elementData; // non-private to simplify nested class access /** * The size of the ArrayList (the number of elements it contains). * * @serial */ // 實際數(shù)據(jù)的數(shù)量 private int size;
底層使用數(shù)組保存所有元素
transient 如果用transient聲明一個實例變量,當(dāng)對象存儲時,它的值不需要維持。換句話來說就是,用transient關(guān)鍵字標(biāo)記的成員變量不參與序列化過程
/** * Shared empty array instance used for default sized empty instances. We * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when * first element is added. */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * Constructs an empty list with an initial capacity of ten. */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
默認(rèn)情況下初始化空數(shù)組(長度為0的數(shù)組)
/** * Shared empty array instance used for empty instances. */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * Constructs an empty list with the specified initial capacity. * * @param initialCapacity the initial capacity of the list * @throws IllegalArgumentException if the specified initial capacity * is negative */ 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); } }
指定數(shù)組的初始容量
當(dāng)指定的初始容量大于0,初始化指定大小的數(shù)組
當(dāng)指定的初始容量等于0,初始化空數(shù)組
當(dāng)指定的初始容量小于0,拋出IllegalArgumentException異常
/** * Constructs a list containing the elements of the specified * collection, in the order they are returned by the collection"s * iterator. * * @param c the collection whose elements are to be placed into this list * @throws NullPointerException if the specified collection is null */ public ArrayList(Collection extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } }
初始化指定集合的數(shù)組
當(dāng)指定集合不為空即長度不為0,則復(fù)制該集合,否則初始化一個空數(shù)組
// Positional Access Operations // 返回index下標(biāo)的元素且強(qiáng)制轉(zhuǎn)化為E(List中的E)類型 @SuppressWarnings("unchecked") E elementData(int index) { return (E) elementData[index]; } /** * Returns the element at the specified position in this list. * * @param index index of the element to return * @return the element at the specified position in this list * @throws IndexOutOfBoundsException {@inheritDoc} */ public E get(int index) { // 檢查index是否越界 rangeCheck(index); // 返回index下標(biāo)的元素 return elementData(index); } /** * Checks if the given index is in range. If not, throws an appropriate * runtime exception. This method does *not* check if the index is * negative: It is always used immediately prior to an array access, * which throws an ArrayIndexOutOfBoundsException if index is negative. */ private void rangeCheck(int index) { // 檢查index是否大于等于size(數(shù)組的元素數(shù)量),因為數(shù)組下標(biāo)從0開始計算,所以也不能等于元素數(shù)量 // 這里沒有檢查index < 0的情況,因為index < 0時數(shù)組會自動拋出異常,所以并未檢查index<0的情況 if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } /** * Constructs an IndexOutOfBoundsException detail message. * Of the many possible refactorings of the error handling code, * this "outlining" performs best with both server and client VMs. */ private String outOfBoundsMsg(int index) { return "Index: "+index+", Size: "+size; }
首先判斷index是否越界,這里并沒有判斷是否小于0,因為下標(biāo)小于0時數(shù)組會拋出異常。越界則拋出IndexOutOfBoundsException異常,反之返回數(shù)組對應(yīng)index位置的元素
E set(int index, E element) 設(shè)置(覆蓋)index位置的元素/** * Replaces the element at the specified position in this list with * the specified element. * * @param index index of the element to replace * @param element element to be stored at the specified position * @return the element previously at the specified position * @throws IndexOutOfBoundsException {@inheritDoc} */ public E set(int index, E element) { rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue; }
和get一樣先判斷index(下標(biāo))是否越界,不越界則先獲取原來index位置上的元素,接著設(shè)置(覆蓋)index位置上的元素,然后返回原來的元素,反之拋出IndexOutOfBoundsException異常
boolean add(E e) 添加一個元素到列表尾/** * Appends the specified element to the end of this list. * * @param e element to be appended to this list * @return true (as specified by {@link Collection#add}) */ public boolean add(E e) { // 檢查當(dāng)前容量是否還可以容納一個元素,不夠則擴(kuò)容 ensureCapacityInternal(size + 1); // Increments modCount!! // 添加到數(shù)組末尾 // 這個語句可以分解為 // elementData[size] = e; // size += 1; elementData[size++] = e; return true; } /** * Default initial capacity. */ private static final int DEFAULT_CAPACITY = 10; // 默認(rèn)容量為10 // 如果數(shù)據(jù)等于默認(rèn)數(shù)據(jù),返回默認(rèn)容量和minCapacity(所需容量最小值)的最大值,反之返回所需容量最小值 private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private void ensureExplicitCapacity(int minCapacity) { modCount++; // 操作數(shù)+1 // overflow-conscious code // 如果所需容量最小值大于實際數(shù)組的長度就擴(kuò)大實際數(shù)組容量 if (minCapacity - elementData.length > 0) grow(minCapacity); } /** * The maximum size of array to allocate. * Some VMs reserve some header words in an array. * Attempts to allocate larger arrays may result in * OutOfMemoryError: Requested array size exceeds VM limit */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; // 數(shù)組最大容量為Integer最大值-8 /** * Increases the capacity to ensure that it can hold at least the * number of elements specified by the minimum capacity argument. * * @param minCapacity the desired minimum capacity */ private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; // 新的容量為舊的容量的1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1); // 如果擴(kuò)充容量后還是不夠,則新的容量等于所需容量最小值(一般就是數(shù)組實際元素個數(shù)) if (newCapacity - minCapacity < 0) newCapacity = minCapacity; // 如果新的容量大于數(shù)組最大容量,再調(diào)用hugeCapacity計算新的容量 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: // 復(fù)制原來的數(shù)據(jù)到新的數(shù)組,數(shù)組容量為新的容量 elementData = Arrays.copyOf(elementData, newCapacity); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); // 大于數(shù)組最大容量返回Integer最大值,反之返回數(shù)組最大容量 return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
添加一個元素到列表尾,當(dāng)列表容量不足時自動擴(kuò)容(通常是擴(kuò)容至原來的1.5倍),添加成功返回true
void add(int index, E element) 在index處放置元素/** * Inserts the specified element at the specified position in this * list. Shifts the element currently at that position (if any) and * any subsequent elements to the right (adds one to their indices). * * @param index index at which the specified element is to be inserted * @param element element to be inserted * @throws IndexOutOfBoundsException {@inheritDoc} */ public void add(int index, E element) { // 檢查下標(biāo)是否越界 rangeCheckForAdd(index); // 檢查當(dāng)前容量是否還可以在容納一個元素,不夠則擴(kuò)容 ensureCapacityInternal(size + 1); // Increments modCount!! // 將elementData從index開始后面的元素往后移一位 System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; } /** * A version of rangeCheck used by add and addAll. */ private void rangeCheckForAdd(int index) { // 當(dāng)index等于size時相當(dāng)于添加元素到列表尾 if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }
將elementData數(shù)組從index開始后面的元素往后移一位,接著在index處放置元素
模擬添加數(shù)據(jù)(lierabbit)到index=4過程如下
/** * Appends all of the elements in the specified collection to the end of * this list, in the order that they are returned by the * specified collection"s Iterator. The behavior of this operation is * undefined if the specified collection is modified while the operation * is in progress. (This implies that the behavior of this call is * undefined if the specified collection is this list, and this * list is nonempty.) * * @param c collection containing elements to be added to this list * @return true if this list changed as a result of the call * @throws NullPointerException if the specified collection is null */ public boolean addAll(Collection extends E> c) { Object[] a = c.toArray(); int numNew = a.length; // 檢查當(dāng)前容量是否還可以在容納a數(shù)組的元素,不夠則擴(kuò)容 ensureCapacityInternal(size + numNew); // Increments modCount // 將a數(shù)組里的元素添加到elementData末尾 System.arraycopy(a, 0, elementData, size, numNew); size += numNew; // a數(shù)組不為空(長度不為0)時返回true,反之false return numNew != 0; }
將要添加的集合變?yōu)閿?shù)組,然后將其復(fù)制到elementData數(shù)組末尾
boolean addAll(int index, Collection extends E> c) 添加一個集合里的所有元素到index位置/** * Inserts all of the elements in the specified collection into this * list, starting at the specified position. Shifts the element * currently at that position (if any) and any subsequent elements to * the right (increases their indices). The new elements will appear * in the list in the order that they are returned by the * specified collection"s iterator. * * @param index index at which to insert the first element from the * specified collection * @param c collection containing elements to be added to this list * @return true if this list changed as a result of the call * @throws IndexOutOfBoundsException {@inheritDoc} * @throws NullPointerException if the specified collection is null */ public boolean addAll(int index, Collection extends E> c) { // 檢查下標(biāo)是否越界 rangeCheckForAdd(index); Object[] a = c.toArray(); int numNew = a.length; // 檢查當(dāng)前容量是否還可以在容納a數(shù)組的元素,不夠則擴(kuò)容 ensureCapacityInternal(size + numNew); // Increments modCount // 需要往后移動幾個位置 int numMoved = size - index; if (numMoved > 0) // 從index開始,往后的元素向后移動numMoved個位置 System.arraycopy(elementData, index, elementData, index + numNew, numMoved); // 將a數(shù)組里的所有元素復(fù)制到elementData從index到index + numNew -1的位置上 System.arraycopy(a, 0, elementData, index, numNew); size += numNew; // a數(shù)組不為空(長度不為0)時返回true,反之false return numNew != 0; }
將要添加的集合變?yōu)閿?shù)組,然后把elementData數(shù)組從index開始,往后的元素向后移動numMoved個位置,接著將要添加的數(shù)組里的所有元素復(fù)制到elementData從index到index + numNew -1的位置上
void trimToSize() 改變列表內(nèi)部數(shù)組容量至列表實際元素數(shù)量/** * Trims the capacity of this ArrayList instance to be the * list"s current size. An application can use this operation to minimize * the storage of an ArrayList instance. */ public void trimToSize() { modCount++; // 操作數(shù)+1 // 如果數(shù)組實際元素數(shù)量小于數(shù)組長度 if (size < elementData.length) { // 如果數(shù)組實際元素數(shù)量等于0則數(shù)組被賦值為空數(shù)組,反之創(chuàng)建一個新的元素數(shù)量等于數(shù)組長度的數(shù)組 elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size); } }
當(dāng)數(shù)據(jù)穩(wěn)定了之后可以使用這個方法來減少內(nèi)存的使用
int indexOf(Object o) 查找o元素在列表第一次出現(xiàn)的位置/** * Returns the index of the first occurrence of the specified element * in this list, or -1 if this list does not contain the element. * More formally, returns the lowest index i such that * (o==null ? get(i)==null : o.equals(get(i))), * or -1 if there is no such index. */ public int indexOf(Object o) { //元素可以為null,如果為null返回null的下標(biāo) 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; } // 沒有找到對應(yīng)的元素返回-1 return -1; }
ArrayList中可以存放null元素,indexof是返回elementData數(shù)組中值相同的首個元素的下標(biāo),indexof中比較方法是equals而equals是比較元素的值,因此必須對null多帶帶查找。如果未找到該元素則返回-1
E remove(int index) 刪除index位置上的元素/** * Removes the element at the specified position in this list. * Shifts any subsequent elements to the left (subtracts one from their * indices). * * @param index the index of the element to be removed * @return the element that was removed from the list * @throws IndexOutOfBoundsException {@inheritDoc} */ public E remove(int index) { // 檢查下標(biāo)是否越界 rangeCheck(index); modCount++; // 操作數(shù)+1 E oldValue = elementData(index); // 獲取index位置上的元素 int numMoved = size - index - 1; // 需要往前移動幾個位置 if (numMoved > 0) // 從index + 1開始,往后的元素向前移動1個位置 System.arraycopy(elementData, index+1, elementData, index, numMoved); // 將數(shù)組末尾元素置空 elementData[--size] = null; // clear to let GC do its work return oldValue; }
模擬刪除index=4(值為lierabbit)過程如下
/** * Removes the first occurrence of the specified element from this list, * if it is present. If the list does not contain the element, it is * unchanged. More formally, removes the element with the lowest index * i such that * (o==null ? get(i)==null : o.equals(get(i))) * (if such an element exists). Returns true if this list * contained the specified element (or equivalently, if this list * changed as a result of the call). * * @param o element to be removed from this list, if present * @return true if this list contained the specified element */ public boolean remove(Object o) { // 元素可以為null,分開搜索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; } } // 沒有找到返回false return false; } /* * Private remove method that skips bounds checking and does not * return the value removed. */ // 由于已經(jīng)找到元素,則元素必定存在,則index必定合理,所以不需要在檢查index是否越界 private void fastRemove(int index) { modCount++; int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work }
通過尋找o元素,可以獲得其下標(biāo),再根據(jù)下標(biāo)刪除o元素
forEach(Consumer super E> action)遍歷列表/** * The number of times this list has been structurally modified. * Structural modifications are those that change the size of the * list, or otherwise perturb it in such a fashion that iterations in * progress may yield incorrect results. * *This field is used by the iterator and list iterator implementation * returned by the {@code iterator} and {@code listIterator} methods. * If the value of this field changes unexpectedly, the iterator (or list * iterator) will throw a {@code ConcurrentModificationException} in * response to the {@code next}, {@code remove}, {@code previous}, * {@code set} or {@code add} operations. This provides * fail-fast behavior, rather than non-deterministic behavior in * the face of concurrent modification during iteration. * *
Use of this field by subclasses is optional. If a subclass * wishes to provide fail-fast iterators (and list iterators), then it * merely has to increment this field in its {@code add(int, E)} and * {@code remove(int)} methods (and any other methods that it overrides * that result in structural modifications to the list). A single call to * {@code add(int, E)} or {@code remove(int)} must add no more than * one to this field, or the iterators (and list iterators) will throw * bogus {@code ConcurrentModificationExceptions}. If an implementation * does not wish to provide fail-fast iterators, this field may be * ignored. */ protected transient int modCount = 0;//操作數(shù) @Override public void forEach(Consumer super E> action) { // 確保不為空 Objects.requireNonNull(action); final int expectedModCount = modCount; @SuppressWarnings("unchecked") final E[] elementData = (E[]) this.elementData; final int size = this.size; for (int i=0; modCount == expectedModCount && i < size; i++) { action.accept(elementData[i]); } if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } } /** * Checks that the specified object reference is not {@code null}. This * method is designed primarily for doing parameter validation in methods * and constructors, as demonstrated below: *
* * @param obj the object reference to check for nullity * @param* public Foo(Bar bar) { * this.bar = Objects.requireNonNull(bar); * } *the type of the reference * @return {@code obj} if not {@code null} * @throws NullPointerException if {@code obj} is {@code null} */ public static T requireNonNull(T obj) { if (obj == null) throw new NullPointerException(); return obj; }
這里可以看到modCount的用處,當(dāng)modCount發(fā)生改變后,立刻拋出ConcurrentModificationException異常。通過之前的分析可以知道當(dāng)列表內(nèi)容被修改時modCount會增加。也就是說如果在遍歷ArrayList的過程中有其他線程修改了ArrayList,那么將拋出ConcurrentModificationException異常
ArrayList小結(jié)ArrayList是List接口的一個可變大小的數(shù)組的實現(xiàn)
ArrayList的內(nèi)部是使用一個Object對象數(shù)組來存儲元素的
初始化ArrayList的時候,可以指定初始化容量的大小,如果不指定,就會使用默認(rèn)大小,為10
當(dāng)添加一個新元素的時候,首先會檢查容量是否足夠添加這個元素,如果夠就直接添加,如果不夠就進(jìn)行擴(kuò)容,擴(kuò)容為原數(shù)組容量的1.5倍
當(dāng)在index處放置一個元素的時候,會將數(shù)組index處右邊的元素全部右移
當(dāng)在index處刪除一個元素的時候,會將數(shù)組index處右邊的元素全部左移
本文首發(fā):https://lierabbit.cn/2018/01/...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/68371.html
摘要:靜態(tài)變量是被泛型類的所有實例所共享的。所以引用能完成泛型類型的檢查。對于這個類型系統(tǒng),有如下的一些規(guī)則相同類型參數(shù)的泛型類的關(guān)系取決于泛型類自身的繼承體系結(jié)構(gòu)。事實上,泛型類擴(kuò)展都不合法。 前言 和C++以模板來實現(xiàn)靜多態(tài)不同,Java基于運(yùn)行時支持選擇了泛型,兩者的實現(xiàn)原理大相庭徑。C++可以支持基本類型作為模板參數(shù),Java卻只能接受類作為泛型參數(shù);Java可以在泛型類的方法中取得...
摘要:加載因子是哈希表在其容量自動增加之前可以達(dá)到多滿的一種尺度。當(dāng)哈希表中的條目數(shù)超出了加載因子與當(dāng)前容量的乘積時,則要對該哈希表進(jìn)行操作即重建內(nèi)部數(shù)據(jù)結(jié)構(gòu),從而哈希表將具有大約兩倍的桶數(shù)。成員變量每個對由封裝,存在了對象數(shù)組中。 雖是讀書筆記,但是如轉(zhuǎn)載請注明出處 http://segmentfault.com/blog/exploring/ .. 拒絕伸手復(fù)制黨 LinkedLis...
摘要:近段時間在準(zhǔn)備實習(xí)的面試,在網(wǎng)上看到一份面試題,就慢慢試著做,爭取每天積累一點點?,F(xiàn)在每天給自己在面試題編寫的任務(wù)是題,有時候忙起來可能就沒有時間寫了,但是爭取日更,即使當(dāng)天沒更也會在之后的更新補(bǔ)上。 ????近段時間在準(zhǔn)備實習(xí)的面試,在網(wǎng)上看到一份面試題,就慢慢試著做,爭取每天積累一點點。????暫時手頭上的面試題只有一份,題量還是挺大的,有208題,所以可能講的不是很詳細(xì),只是我自...
摘要:根據(jù)拇指規(guī)則,最佳做法應(yīng)該是盡量減少屬性的訪問級別。通常的,可變對象可用來避免產(chǎn)生過多的對象。如果類中定義了構(gòu)造函數(shù),那么編譯器將不會給它插入默認(rèn)構(gòu)造函數(shù)。 1、轉(zhuǎn)化數(shù)組為ArrayList 通常開發(fā)者轉(zhuǎn)化數(shù)組為ArrayList的方式為 List list = Arrays.asList(arr); Arrays.asList()會返回一個ArrayList,而這個ArrayList...
閱讀 2109·2023-04-26 02:41
閱讀 2153·2021-09-24 09:47
閱讀 1560·2019-08-30 15:53
閱讀 1214·2019-08-30 13:01
閱讀 1894·2019-08-29 11:27
閱讀 2868·2019-08-28 17:55
閱讀 1779·2019-08-26 14:00
閱讀 3394·2019-08-26 10:18