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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java集合-ArrayList源码解析-JDK1.8

發布時間:2023/12/20 java 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java集合-ArrayList源码解析-JDK1.8 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.


ArrayList簡介

ArrayList 是一個數組隊列,相當于 動態數組。與Java中的數組相比,它的容量能動態增長。它繼承于AbstractList,實現了List, RandomAccess, Cloneable, java.io.Serializable這些接口。

  • AbstractList、List提供了添加、刪除、修改、遍歷等功能。

  • RandmoAccess提供了隨機訪問功能

  • Cloneable提供了可以被克隆的功能

  • Serializable提供了序列化的功能

  • 和Vector不同,ArrayList中的操作不是線程安全的!所以,建議在單線程中才使用ArrayList,而在多線程中可以選擇Vector或CopyOnWriteArrayList。


  • ArrayList的屬性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    /**
    * 數組默認的大小
    */
    private static final int DEFAULT_CAPACITY = 10;

    /**
    * 使用數組大小為0時的默認緩沖區
    */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
    * 使用ArrayList(int initialCapacity)構造方法時且initialCapacity為0時緩沖區
    */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
    * 真實存儲arraylist元素的數組緩沖區
    */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
    * List的實際大小
    */
    private int size;
    /**
    * 數組可分配的最大大小
    */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    /**
    * ?特別注意這個是繼承自AbstractList的屬性,用來記錄List被修改的次數
    */
    protected transient int modCount = 0;


    ArrayList的構造方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35

    /**
    * 無參構造方法,初始化elementData
    */
    public ArrayList() {
    ? ?this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
    * 根據參數構建具有初始大小的構造方法
    */
    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);
    ? ?}
    }
    /**
    * 創建一個包含collection的ArrayList
    */
    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;
    ? ?}
    }


    ArrayList的方法

    接下來我們就以ArrayList的幾個比較經典的方法來看一下它是如何設計的。

    首先是添加方法,添加的方法一共有3個:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    /**
    ? ?* 添加元素
    ? ?*/
    ? public boolean add(E e) {
    ? ? ? //計算數組最新的容量,以及判斷是否需要擴容
    ? ? ? ensureCapacityInternal(size + 1); ?// Increments modCount!!
    ? ? ? elementData[size++] = e;
    ? ? ? return true;
    ? }

    ? /**
    ? ?* 指定索引添加元素
    ? ?*/
    ? public void add(int index, E element) {
    ? ? ? //判斷索引是否越界
    ? ? ? rangeCheckForAdd(index);
    ? ? ? //計算數組最新的容量,以及判斷是否需要擴容
    ? ? ? ensureCapacityInternal(size + 1); ?// Increments modCount!!
    ? ? ? //調用系統底層的復制方法
    ? ? ? System.arraycopy(elementData, index, elementData, index + 1,
    ? ? ? ? ? ? ? size - index);
    ? ? ? elementData[index] = element;
    ? ? ? //List長度+1
    ? ? ? size++;
    ? }
    ? ?/**
    ? ?* 添加一個集合
    ? ?*/
    ? public boolean addAll(Collection<? extends E> c) {
    ? ? ? Object[] a = c.toArray();
    ? ? ? int numNew = a.length;
    ? ? ? ensureCapacityInternal(size + numNew); ?// Increments modCount
    ? ? ? System.arraycopy(a, 0, elementData, size, numNew);
    ? ? ? size += numNew;
    ? ? ? return numNew != 0;
    ? }

    仔細觀察上方三個添加的方法,它們都調用了ensureCapacityInternal方法,這個方法的參數是執行當前添加操作所需要的數組容量。它會根據傳遞的參數來計算數組是否需要擴容,如果需要擴容則完成擴容操作。
    不同之處在于,上方的兩個方法添加的只有一個元素,所以傳的size+1,而addAll因為是添加的一個集合所以傳的參數是size+集合的長度。

    接著看這個方法的實現:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    /**
    ? ?* 計算數組最新的容量
    ? ?* @param minCapacity
    ? ?*/
    ? private void ensureCapacityInternal(int minCapacity) {
    ? ? ? //如果創建ArrayList時指定大小為0
    ? ? ? if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
    ? ? ? ? ? //如果本次添加的大小比初始容量10大的話則不使用默認的容量10,直接使用本次添加的大小作為初始容量
    ? ? ? ? ? minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    ? ? ? }

    ? ? ? ensureExplicitCapacity(minCapacity);
    ? }

    ? /**
    ? ?* 記錄修改次數,調用擴容方法
    ? ?* @param minCapacity
    ? ?*/
    ? private void ensureExplicitCapacity(int minCapacity) {
    ? ? ? modCount++;

    ? ? ? // overflow-conscious code
    ? ? ? if (minCapacity - elementData.length > 0)
    ? ? ? ? ? //擴容
    ? ? ? ? ? grow(minCapacity);
    ? }

    ? /**
    ? ?* 擴容
    ? ?*/
    ? private void grow(int minCapacity) {
    ? ? ? // 獲取原來的數組長度
    ? ? ? int oldCapacity = elementData.length;
    ? ? ? //新容量設置為老容量的1.5倍
    ? ? ? int newCapacity = oldCapacity + (oldCapacity >> 1);
    ? ? ? //如果新容量還不夠存放本次需要添加的大小,則直接擴容到本次添加的大小
    ? ? ? if (newCapacity - minCapacity < 0)
    ? ? ? ? ? newCapacity = minCapacity;
    ? ? ? //如果新容量超出數組最大容量
    ? ? ? if (newCapacity - MAX_ARRAY_SIZE > 0)
    ? ? ? ? ? newCapacity = hugeCapacity(minCapacity);
    ? ? ? // 調用Arrays的復制方法更新數據緩沖池
    ? ? ? elementData = Arrays.copyOf(elementData, newCapacity);
    ? }
    ? ? //判斷容量是否溢出
    ? private static int hugeCapacity(int minCapacity) {
    ? ? ? if (minCapacity < 0) // overflow
    ? ? ? ? ? throw new OutOfMemoryError();
    ? ? ? return (minCapacity > MAX_ARRAY_SIZE) ?
    ? ? ? ? ? ? ? Integer.MAX_VALUE :
    ? ? ? ? ? ? ? MAX_ARRAY_SIZE;
    ? }

    以上就是ArrayList動態擴容的實現方式了,這里注意一下擴容是通過新建一個數組來替換原先的數組來進行的:

    1
    elementData = Arrays.copyOf(elementData, newCapacity);

    接下來看刪除操作:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    /**
    ? ?* 遍歷數組,找出需要刪除的元素的索引,并調用刪除方法
    ? ?*/
    ? 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;
    ? }
    /**
    ? ?* 刪除指定索引的元素
    ? ?*
    ? ?*/
    ? 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);
    ? ? ? //數組長度減一,刪除數組最后一個位置的元素
    ? ? ? elementData[--size] = null; // clear to let GC do its work

    ? ? ? return oldValue;
    ? }
    ? /*
    ? ?* 刪除指定元素
    ? ?*/
    ? 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
    ? }

    需要注意的是刪除一個元素也是通過底層的方法實現的。

    接著看get和set相對就比較簡單了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public E get(int index) {
    ? ? ? //判斷索引是否越界
    ? ? ? rangeCheck(index);
    ? ? ? return elementData(index);
    ? }
    ? ?/**
    ? ?* 判斷索引是否越界
    ? ?*/
    ? private void rangeCheck(int index) {
    ? ? ? if (index >= size)
    ? ? ? ? ? throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    ? }
    ? public E set(int index, E element) {
    ? ? ? //判斷索引是否越界
    ? ? ? rangeCheck(index);
    ? ? ? //獲取此索引原先的值
    ? ? ? E oldValue = elementData(index);
    ? ? ? elementData[index] = element;
    ? ? ? return oldValue;
    ? }

    看了ArrayList的增刪改查方法相信你已經明白了為什么一直有人告訴你ArrayList查詢修改效率高而添加和刪除效率低了。

    ArrayList的序列化方式同樣是比較有意思的,一開始看到ArrayList實現了Serializable我們就知道它是可以序列化的,但是實際存儲的數組elementData卻是transient,觀看下方代碼你就可以找到答案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    /**
    ? * 將List寫入s,注意先寫容量,然后在寫數據
    ? * @param s
    ? * @throws java.io.IOException
    ? */
    ?private void writeObject(java.io.ObjectOutputStream s)
    ? ? ? ? ?throws java.io.IOException{
    ? ? ?// Write out element count, and any hidden stuff
    ? ? ?int expectedModCount = modCount;
    ? ? ?s.defaultWriteObject();

    ? ? ?// 首先寫數組容量
    ? ? ?s.writeInt(size);

    ? ? ?// 遍歷寫數組中的元素
    ? ? ?for (int i=0; i<size; i++) {
    ? ? ? ? ?s.writeObject(elementData[i]);
    ? ? ?}

    ? ? ?if (modCount != expectedModCount) {
    ? ? ? ? ?throw new ConcurrentModificationException();
    ? ? ?}
    ?}

    ?/**
    ? * 讀取s中的List
    ? */
    ?private void readObject(java.io.ObjectInputStream s)
    ? ? ? ? ?throws java.io.IOException, ClassNotFoundException {
    ? ? ?elementData = EMPTY_ELEMENTDATA;

    ? ? ?// Read in size, and any hidden stuff
    ? ? ?s.defaultReadObject();

    ? ? ?// 首先讀數組容量
    ? ? ?s.readInt(); // ignored

    ? ? ?if (size > 0) {
    ? ? ? ? ?// be like clone(), allocate array based upon size not capacity
    ? ? ? ? ?ensureCapacityInternal(size);

    ? ? ? ? ?Object[] a = elementData;
    ? ? ? ? ?// Read in all elements in the proper order.
    ? ? ? ? ?for (int i=0; i<size; i++) {
    ? ? ? ? ? ? ?a[i] = s.readObject();
    ? ? ? ? ?}
    ? ? ?}
    ?}

    鑒于篇幅有限,本篇文章僅列出上方部分代碼,ArrayList完整源碼解析請看:https://github.com/shiyujun/syj-study-demo!!!

    博客所有文章首發于公眾號《Java學習錄》轉載請保留
    掃碼關注公眾號即可領取2000GJava學習資源



    轉載于:https://blog.51cto.com/12980017/2372727

    總結

    以上是生活随笔為你收集整理的Java集合-ArrayList源码解析-JDK1.8的全部內容,希望文章能夠幫你解決所遇到的問題。

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