日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【数据结构】ArrayList原理及实现学习总结

發(fā)布時間:2025/3/8 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【数据结构】ArrayList原理及实现学习总结 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
版權聲明:本文為博主原創(chuàng)文章,未經博主允許不得轉載。 https://blog.csdn.net/qq_34173549/article/details/79936622

一、ArrayList介紹

ArrayList是一種線性數(shù)據(jù)結構,它的底層是用數(shù)組實現(xiàn)的,相當于動態(tài)數(shù)組。與Java中的數(shù)組相比,它的容量能動態(tài)增長。類似于C語言中的動態(tài)申請內存,動態(tài)增長內存。?
當創(chuàng)建一個數(shù)組的時候,就必須確定它的大小,系統(tǒng)會在內存中開辟一塊連續(xù)的空間,用來保存數(shù)組,因此數(shù)組容量固定且無法動態(tài)改變。ArrayList在保留數(shù)組可以快速查找的優(yōu)勢的基礎上,彌補了數(shù)組在創(chuàng)建后,要往數(shù)組添加元素的弊端。實現(xiàn)的基本方法如下:?
1. 快速查找:在物理內存上采用順序存儲結構,因此可根據(jù)索引快速的查找元素。?
2. 容量動態(tài)增長: 當數(shù)組容量不夠用時(表1),創(chuàng)建一個比原數(shù)組容量大的新數(shù)組(表2),將數(shù)組中的元素“搬”到新數(shù)組(表3),再將新的元素也放入新數(shù)組(表4),最后將新數(shù)組賦給原數(shù)組即可。(從左到右依次為表1,表2、表3、表4)

二、ArrayList繼承關系

ArrayList繼承于AbstractList,實現(xiàn)了List, RandomAccess, Cloneable, java.io.Serializable這些接口。?
實現(xiàn)了所有List接口的操作,并ArrayList允許存儲null值。除了沒有進行同步,ArrayList基本等同于Vector。在Vector中幾乎對所有的方法都進行了同步,但ArrayList僅對writeObject和readObject進行了同步,其它比如add(Object)、remove(int)等都沒有同步。

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {}
  • 1
  • 2

ArrayList與Collection關系如下圖,實線代表繼承,虛線代表實現(xiàn)接口:?

  • AbstractList提供了List接口的默認實現(xiàn)(個別方法為抽象方法)。
  • List接口定義了列表必須實現(xiàn)的方法。
  • 實現(xiàn)了RandomAccess接口:提供了隨機訪問功能。RandmoAccess是java中用來被List實現(xiàn),為List提供快速訪問功能的。在ArrayList中,我們即可以通過元素的序號快速獲取元素對象;這就是快速隨機訪問。
  • 實現(xiàn)了Cloneable接口:可以調用Object.clone方法返回該對象的淺拷貝。
  • 實現(xiàn)了 java.io.Serializable 接口:可以啟用其序列化功能,能通過序列化去傳輸。未實現(xiàn)此接口的類將無法使其任何狀態(tài)序列化或反序列化。序列化接口沒有方法或字段,僅用于標識可序列化的語義。
  • 三、ArrayList的實現(xiàn)

    對于ArrayList而言,它實現(xiàn)List接口、底層使用數(shù)組保存所有元素。其操作基本上是對數(shù)組的操作。下面進行具體的介紹:

    1. 私有屬性

    // 保存ArrayList中數(shù)據(jù)的數(shù)組 private transient Object[] elementData; // ArrayList中實際數(shù)據(jù)的數(shù)量 private int size;
    • 1
    • 2
    • 3
    • 4

    很容易理解,elementData存儲ArrayList內的元素,size表示它包含的元素的數(shù)量。?
    有個關鍵字需要解釋:transient。?
    Java的serialization提供了一種持久化對象實例的機制。當持久化對象時,可能有一個特殊的對象數(shù)據(jù)成員,我們不想用serialization機制來保存它。為了在一個特定對象的一個域上關閉serialization,可以在這個域前加上關鍵字transient。

    2.構造函數(shù)

    ArrayList提供了三種方式的構造器,可以構造一個指定初始容量的空列表、構造一個默認初始容量為10的空列表以及構造一個包含指定collection的元素的列表,這些元素按照該collection的迭代器返回它們的順序排列的。

    // ArrayList帶容量大小的構造函數(shù)。public ArrayList(int initialCapacity) {super();if (initialCapacity < 0)throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);// 新建一個數(shù)組this.elementData = new Object[initialCapacity];}// ArrayList構造函數(shù)。默認容量是10。public ArrayList() {this(10);}// 創(chuàng)建一個包含collection的ArrayListpublic ArrayList(Collection<? extends E> c) {elementData = c.toArray();size = elementData.length;// c.toArray might (incorrectly) not return Object[] (see 6260652)if (elementData.getClass() != Object[].class)elementData = Arrays.copyOf(elementData, size, Object[].class);}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    3.元素存儲?
    ArrayList是基于數(shù)組實現(xiàn)的,當添加元素的時候,如果數(shù)組大,則在將某個位置的值設置為指定元素即可,如果數(shù)組容量不夠了,以add(E e)為例,可以看到add(E e)中先調用了ensureCapacity(size+1)方法,之后將元素的索引賦給elementData[size],而后size自增。例如初次添加時,size為0,add將elementData[0]賦值為e,然后size設置為1(類似執(zhí)行以下兩條語句elementData[0]=e;size=1)。將元素的索引賦給elementData[size]不是會出現(xiàn)數(shù)組越界的情況嗎?這里關鍵就在ensureCapacity(size+1)中了。?
    具體實現(xiàn)如下:?
    (1)?當調用下面這兩個方法向數(shù)組中添加元素時,默認是添加到數(shù)組中最后一個元素的后面。內存結構變化如下:?

    // 添加元素epublic boolean add(E e) {// 確定ArrayList的容量大小ensureCapacity(size + 1); // Increments modCount!!// 添加e到ArrayList中elementData[size++] = e;return true;} // 將集合c追加到ArrayList中public boolean addAll(Collection<? extends E> c) {Object[] a = c.toArray();int numNew = a.length;ensureCapacity(size + numNew); // Increments modCountSystem.arraycopy(a, 0, elementData, size, numNew);size += numNew;return numNew != 0;}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    (2)當調用下面這兩個方法向數(shù)組中添加元素或集合時,會先查找索引位置,然后將元素添加到索引處,最后把添加前索引后面的元素追加到新元素的后面。?

    // 將e添加到ArrayList的指定位置public void add(int index, E element) {if (index > size || index < 0)throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);ensureCapacity(size + 1); // Increments modCount!!System.arraycopy(elementData, index, elementData, index + 1, size - index);elementData[index] = element;size++;}// 從index位置開始,將集合c添加到ArrayListpublic boolean addAll(int index, Collection<? extends E> c) {if (index > size || index < 0)throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);Object[] a = c.toArray();int numNew = a.length;ensureCapacity(size + numNew); // Increments modCountint numMoved = size - index;if (numMoved > 0)System.arraycopy(elementData, index, elementData, index + numNew, numMoved);System.arraycopy(a, 0, elementData, index, numNew);size += numNew;return numNew != 0;}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    (3)調用該方法會將index位置的元素用新元素替代?

    // 設置index位置的值為elementpublic E set(int index, E element) {RangeCheck(index);E oldValue = (E) elementData[index];elementData[index] = element;return oldValue;}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    4.元素讀取

    // 返回此列表中指定位置上的元素。public E get(int index) {RangeCheck(index);return (E) elementData[index];}
    • 1
    • 2
    • 3
    • 4
    • 5

    5.元素刪除?
    ArrayList提供了根據(jù)下標或者指定對象兩種方式的刪除功能。如下:?
    romove(int index),首先是檢查范圍,修改modCount,保留將要被移除的元素,將移除位置之后的元素向前挪動一個位置,將list末尾元素置空(null),返回被移除的元素。?

    // 刪除ArrayList指定位置的元素public E remove(int index) {RangeCheck(index);modCount++;E oldValue = (E) elementData[index];int numMoved = size - index - 1;if (numMoved > 0)System.arraycopy(elementData, index + 1, elementData, index, numMoved);elementData[--size] = null; // Let gc do its workreturn oldValue;}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    6. 調整數(shù)組容量ensureCapacity?
    (1)從上面介紹的向ArrayList中存儲元素的代碼中,我們看到,每當向數(shù)組中添加元素時,都要去檢查添加后元素的個數(shù)是否會超出當前數(shù)組的長度,如果超出,數(shù)組將會進行擴容,以滿足添加數(shù)據(jù)的需求。數(shù)組擴容通過一個公開的方法ensureCapacity(int minCapacity)來實現(xiàn)。在實際添加大量元素前,我也可以使用ensureCapacity來手動增加ArrayList實例的容量,以減少遞增式再分配的數(shù)量。

    // 確定ArrarList的容量。// 若ArrayList的容量不足以容納當前的全部元素,設置 新的容量=“(原始容量x3)/2 + 1”public void ensureCapacity(int minCapacity) {// 將“修改統(tǒng)計數(shù)”+1modCount++;int oldCapacity = elementData.length;// 若當前容量不足以容納當前的元素個數(shù),設置 新的容量=“(原始容量x3)/2 + 1”if (minCapacity > oldCapacity) {Object oldData[] = elementData;int newCapacity = (oldCapacity * 3) / 2 + 1;if (newCapacity < minCapacity)newCapacity = minCapacity;elementData = Arrays.copyOf(elementData, newCapacity);}}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    從上述代碼中可以看出,數(shù)組進行擴容時,會將老數(shù)組中的元素重新拷貝一份到新的數(shù)組中,每次數(shù)組容量的增長大約是其原容量的1.5倍。這種操作的代價是很高的,因此在實際使用時,我們應該盡量避免數(shù)組容量的擴張。當我們可預知要保存的元素的多少時,要在構造ArrayList實例時,就指定其容量,以避免數(shù)組擴容的發(fā)生。或者根據(jù)實際需求,通過調用ensureCapacity方法來手動增加ArrayList實例的容量。?
    (2)?ArrayList還給我們提供了將底層數(shù)組的容量調整為當前列表保存的實際元素的大小的功能。它可以通過trimToSize方法來實現(xiàn)。代碼如下:

    // 將當前容量值設為 =實際元素個數(shù)public void trimToSize() {modCount++;int oldCapacity = elementData.length;if (size < oldCapacity) {elementData = Arrays.copyOf(elementData, size);}}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    由于elementData的長度會被拓展,size標記的是其中包含的元素的個數(shù)。所以會出現(xiàn)size很小但elementData.length很大的情況,將出現(xiàn)空間的浪費。trimToSize將返回一個新的數(shù)組給elementData,元素內容保持不變,length和size相同,節(jié)省空間。?
    7.轉為靜態(tài)數(shù)組toArray的兩種方法?
    (1)調用Arrays.copyOf將返回一個數(shù)組,數(shù)組內容是size個elementData的元素,即拷貝elementData從0至size-1位置的元素到新數(shù)組并返回。

    // 返回ArrayList的Object數(shù)組public Object[] toArray() {return Arrays.copyOf(elementData, size);}
    • 1
    • 2
    • 3
    • 4

    (2)如果傳入數(shù)組的長度小于size,返回一個新的數(shù)組,大小為size,類型與傳入數(shù)組相同。所傳入數(shù)組長度與size相等,則將elementData復制到傳入數(shù)組中并返回傳入的數(shù)組。若傳入數(shù)組長度大于size,除了復制elementData外,還將把返回數(shù)組的第size個元素置為空。

    // 返回ArrayList的模板數(shù)組。所謂模板數(shù)組,即可以將T設為任意的數(shù)據(jù)類型public <T> T[] toArray(T[] a) {// 若數(shù)組a的大小 < ArrayList的元素個數(shù);// 則新建一個T[]數(shù)組,數(shù)組大小是“ArrayList的元素個數(shù)”,并將“ArrayList”全部拷貝到新數(shù)組中if (a.length < size)return (T[]) Arrays.copyOf(elementData, size, a.getClass());// 若數(shù)組a的大小 >= ArrayList的元素個數(shù);// 則將ArrayList的全部元素都拷貝到數(shù)組a中。System.arraycopy(elementData, 0, a, 0, size);if (a.length > size)a[size] = null;return a;}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    8.實現(xiàn)了Cloneable接口,進行數(shù)據(jù)淺拷貝

    // 克隆函數(shù)public Object clone() {try {ArrayList<E> v = (ArrayList<E>) super.clone();// 將當前ArrayList的全部元素拷貝到v中v.elementData = Arrays.copyOf(elementData, size);v.modCount = 0;return v;} catch (CloneNotSupportedException e) {// this shouldn't happen, since we are Cloneablethrow new InternalError();}}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    9.實現(xiàn)Serializable 接口,啟用其序列化功能

    // java.io.Serializable的寫入函數(shù)// 將ArrayList的“容量,所有的元素值”都寫入到輸出流中private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {// Write out element count, and any hidden stuffint expectedModCount = modCount;s.defaultWriteObject();// 寫入“數(shù)組的容量”s.writeInt(elementData.length);// 寫入“數(shù)組的每一個元素”for (int i = 0; i < size; i++)s.writeObject(elementData[i]);if (modCount != expectedModCount) {throw new ConcurrentModificationException();}}// java.io.Serializable的讀取函數(shù):根據(jù)寫入方式讀出// 先將ArrayList的“容量”讀出,然后將“所有的元素值”讀出private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {// Read in size, and any hidden stuffs.defaultReadObject();// 從輸入流中讀取ArrayList的“容量”int arrayLength = s.readInt();Object[] a = elementData = new Object[arrayLength];// 從輸入流中將“所有的元素值”讀出for (int i = 0; i < size; i++)a[i] = s.readObject();}

    總結

    以上是生活随笔為你收集整理的【数据结构】ArrayList原理及实现学习总结的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。