<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          ArrayList方法源碼分析

          共 22315字,需瀏覽 45分鐘

           ·

          2021-06-01 23:44

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號”

          優(yōu)質(zhì)文章,第一時間送達(dá)

            作者 |  凝冰物語

          來源 |  urlify.cn/ZbE3eu

          本文將從ArrayList類的存儲結(jié)構(gòu)、初始化、增刪數(shù)據(jù)、擴(kuò)容處理以及元素迭代等幾個方面,分析該類常用方法的源碼。

          數(shù)據(jù)存儲設(shè)計(jì)

          該類用一個Object類型的數(shù)組存儲容器的元素。對于容量為空的情況,提供了兩個成員變量來表示。

          // 用于存儲容器元素的數(shù)組,數(shù)組長度不小于容器內(nèi)元素個數(shù)
          transient Object[] elementData;

          // 容器的大小,反映的是容器內(nèi)實(shí)時元素個數(shù),不超過數(shù)組長度
          private int size;

          // 容器為空的實(shí)例
          private static final Object[] EMPTY_ELEMENTDATA = {};

          // 默認(rèn)構(gòu)造的實(shí)例,用于展示加入第一個元素后容器膨脹的過程
          private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

          初始化

          該類提供了三個初始化方法,如下所示:

          // 基本構(gòu)造方法
          public ArrayList() {
              // 數(shù)組初始化為長度為0的空數(shù)組
              this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
          }

          // 指定初始容量的構(gòu)造方法
          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);
              }
          }

          // 通過已有集合進(jìn)行實(shí)例化的構(gòu)造方法
          public ArrayList(Collection<? extends E> c) {
              elementData = c.toArray();
              if ((size = elementData.length) != 0) {
              
                  // c.toArray might (incorrectly) not return Object[] (see 6260652)
                  // 如果集合轉(zhuǎn)換得到的數(shù)組長度不為0且數(shù)組元素類型不為Object類型,則將其轉(zhuǎn)換為Object類型的數(shù)組
                  if (elementData.getClass() != Object[].class)
                      elementData = Arrays.copyOf(elementData, size, Object[].class);
              } else {
                  // replace with empty array.
                  this.elementData = EMPTY_ELEMENTDATA;
              }
          }

          增刪數(shù)據(jù)

          單元素插入方法,這里沒有對插入元素進(jìn)行非null限制,說明ArrayList容器是可以插入null的。

          /* 在已有元素末尾增加元素 */
          public boolean add(E e) {
              /* 確保數(shù)組的大小至少有size+1的長度,如果小于會對原數(shù)組進(jìn)行擴(kuò)容 */
              ensureCapacityInternal(size + 1);  // Increments modCount!!
              
              /* 在索引為size處添加元素e,同時size+1 */
              elementData[size++] = e;
              return true;
          }

          /* 在數(shù)組索引為index處插入元素 */
          public void add(int index, E element) {
              /* 檢查索引位置,index的范圍為[0,size] */
              rangeCheckForAdd(index);
              
              /* 確保數(shù)組的大小至少有size+1的長度,如果小于會對原數(shù)組進(jìn)行擴(kuò)容 */
              ensureCapacityInternal(size + 1);  // Increments modCount!!
              
              /* 將原來index處及之后的元素全部向后移一位 */
              System.arraycopy(elementData, index, elementData, index + 1,size - index);
              
              /* 將元素element放入index處 */
              elementData[index] = element;
              
              /* 容器內(nèi)元素個數(shù)+1 */
              size++;
          }

          集合插入方法

          // 在已有元素末尾增加集合中所有元素 
          public boolean addAll(Collection<? extends E> c) {
              // 將集合轉(zhuǎn)化為Object數(shù)組 
              Object[] a = c.toArray();
              int numNew = a.length;
              // 確保數(shù)組的大小至少有size+numNew的長度,如果小于會對原數(shù)組進(jìn)行擴(kuò)容
              ensureCapacityInternal(size + numNew);  // Increments modCount
              // 將數(shù)組a內(nèi)元素拷貝到數(shù)組elementData末尾
              System.arraycopy(a, 0, elementData, size, numNew);
              // 數(shù)組大小增加numNew
              size += numNew;
              return numNew != 0;
          }

          // 在索引index處插入集合中所有元素
          public boolean addAll(int index, Collection<? extends E> c) {
              // 檢查索引位置,index的范圍為[0,size] 
              rangeCheckForAdd(index);
              
              Object[] a = c.toArray();
              int numNew = a.length;
              
              // 確保數(shù)組的大小至少有size+numNew的長度,如果小于會對原數(shù)組進(jìn)行擴(kuò)容
              ensureCapacityInternal(size + numNew);  // Increments modCount
              
              // 要移動的元素長度
              int numMoved = size - index;
              // 如果插入位置不在size處,則需要把原數(shù)組[index,size-1]范圍內(nèi)元素移動numMoved長度
              if (numMoved > 0)
                  System.arraycopy(elementData, index, elementData, index + numNew,
                                   numMoved);
              
              System.arraycopy(a, 0, elementData, index, numNew);
              size += numNew;
              return numNew != 0;
          }

          修改元素方法,只提供了修改指定索引下標(biāo)元素的方法

          /* 替換指定索引處元素 */
          public E set(int index, E element) {
              /* 檢查索引位置,index的范圍只能在[0,size-1]內(nèi) */
              rangeCheck(index);
              
              /* 替換index處元素并返回原值 */
              E oldValue = elementData(index);
              elementData[index] = element;
              return oldValue;
          }

          單元素刪除元素方法,提供了根據(jù)索引刪除和查找對象進(jìn)行刪除的方法

          // 刪除指定索引處元素
          public E remove(int index) {
              // 檢查索引范圍,只能在[0,size-1]范圍內(nèi)
              rangeCheck(index);
              
              modCount++;
              // 取出index處待刪除元素
              E oldValue = elementData(index);
              
              // 要移動的元素長度
              int numMoved = size - index - 1;
              // 如果index≠size-1,將index后的元素全部往前移動一位
              if (numMoved > 0)
                  System.arraycopy(elementData, index+1, elementData, index,
                                   numMoved);
              // size數(shù)量先減一
              // 因?yàn)閯h除了一個元素,數(shù)組最后一個元素的引用置為null,讓垃圾收集器去回收元素對象
              elementData[--size] = null; // clear to let GC do its work
              
              // 返回刪除的元素
              return oldValue;
          }

          // 查找對象并刪除元素
          public boolean remove(Object o) {
              // 這里從下標(biāo)0開始遍歷數(shù)組,只刪除遇到的第一個相同元素
              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++)
                      // 因?yàn)閿?shù)組元素可以為null,所以需要用o.equals()以防止NullPointException
                      if (o.equals(elementData[index])) {
                          fastRemove(index);
                          return true;
                      }
              }
              return false;
          }

          // 跳過邊界檢查刪除index索引處元素,確保index處于[0,size-1]范圍內(nèi)
          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
          }

          批量刪除方法

          // 從本集合中刪除與集合C的交集
          public boolean removeAll(Collection<?> c) {
              // 確保集合C非null,否則拋出NullPointException
              Objects.requireNonNull(c);
              return batchRemove(c, false);
          }

          // complement為false表示批量刪除與C的交集元素,complement為true表示只留下和C的交集元素,其它全部刪除
          // 以下說明均采用complement為false的情況,即刪除交集元素
          private boolean batchRemove(Collection<?> c, boolean complement) {
              final Object[] elementData = this.elementData;
              
              // 這里采用了雙指針的思想,r為當(dāng)前遍歷到的位置,w指向?qū)⒁獙懭朐氐奈恢?br>    int r = 0, w = 0;
              
              // 是否有元素被刪除,有則為true,無則為false
              boolean modified = false;
              try {
                  // 遍歷elementDate數(shù)組,如果不在集合c中,則寫入w處,并將w右移一位
                  for (; r < size; r++)
                      if (c.contains(elementData[r]) == complement)
                          elementData[w++] = elementData[r];
              } finally {
                  // Preserve behavioral compatibility with AbstractCollection,
                  // even if c.contains() throws.
                  // 如果沒有遍歷完,即c.contains()拋出了異常或錯誤,需要將r處及其之后的元素復(fù)制到w之后
                  if (r != size) {
                      System.arraycopy(elementData, r,
                                       elementData, w,
                                       size - r);
                      // w向右移動
                      w += size - r;
                  }
                  // 如果w≠size,說明有交集元素被刪除
                  if (w != size) {
                      // clear to let GC do its work
                      // 將w處及之后的引用置為null,讓gc自己回收
                      for (int i = w; i < size; i++)
                          elementData[i] = null;
                      modCount += size - w;
                      size = w;
                      
                      // 有元素被刪除,置為true
                      modified = true;
                  }
              }
              return modified;
          }

          擴(kuò)容處理

          每次在增加元素時,都需要確保數(shù)組的長度要大于容器內(nèi)元素的個數(shù)。比如在add方法中。

          public boolean add(E e) {
              // 使用ensureCapacityInternal方法確保數(shù)組的大小不小于插入元素后的元素個數(shù)
              ensureCapacityInternal(size + 1);  // Increments modCount!!
              elementData[size++] = e;
              return true;
          }

          我們看一下該方法具體的實(shí)現(xiàn),這里擴(kuò)容時會判斷元素組長度的1.5倍是否滿足輸入最小長度要求,滿足的話會按1.5倍容量定義新數(shù)組并復(fù)制元素到新數(shù)組,不滿足則按輸入最小長度擴(kuò)容。

          // 確保數(shù)組長度不低于minCapacity
          private void ensureCapacityInternal(int minCapacity) {
              ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
          }

          private static int calculateCapacity(Object[] elementData, int minCapacity) {
              // 如果數(shù)組為空集,則取默認(rèn)大小(10)和傳入?yún)?shù)中較大的一個
              // 也就是說最小容量為10
              if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                  return Math.max(DEFAULT_CAPACITY, minCapacity);
              }
              return minCapacity;
          }

          private void ensureExplicitCapacity(int minCapacity) {
              modCount++;

              // overflow-conscious code
              // 如果當(dāng)前數(shù)組長度未達(dá)到輸入大小,則進(jìn)行擴(kuò)容操作
              if (minCapacity - elementData.length > 0)
                  grow(minCapacity);
          }

          // 擴(kuò)容操作
          private void grow(int minCapacity) {
              // overflow-conscious code
              int oldCapacity = elementData.length;
              // 原長度的1.5倍
              int newCapacity = oldCapacity + (oldCapacity >> 1);
              // 如果傳入?yún)?shù)超過數(shù)組原長1.5倍,則設(shè)置新長度為傳入?yún)?shù),否則為原長1.5倍
              if (newCapacity - minCapacity < 0)
                  newCapacity = minCapacity;
              // 如果新長度超過設(shè)定最大長度(Integer.MAX_VALUE-8),則獲取更大容量
              if (newCapacity - MAX_ARRAY_SIZE > 0)
                  newCapacity = hugeCapacity(minCapacity);
              // minCapacity is usually close to size, so this is a win:
              elementData = Arrays.copyOf(elementData, newCapacity);
          }

          // 獲取大容量
          private static int hugeCapacity(int minCapacity) {
              // 如果傳入?yún)?shù)超過int最大值,表示擴(kuò)容后的數(shù)組長度溢出了,拋出OOM Error
              if (minCapacity < 0) // overflow
                  throw new OutOfMemoryError();
              // 如果新長度超過設(shè)定最大長度(Integer.MAX_VALUE-8)且不超過Integer.MAX_VALUE,則設(shè)置為Integer.MAX_VALUE
              return (minCapacity > MAX_ARRAY_SIZE) ?
                  Integer.MAX_VALUE :
                  MAX_ARRAY_SIZE;
          }

          元素迭代

          迭代的使用和其它容器類似,通過iterator()方法返回一個Iterator實(shí)例,通過調(diào)用該實(shí)例的hashNext()方法和next()方法進(jìn)行迭代。
          ArrayList類內(nèi)部也定義了實(shí)現(xiàn)了Iterator接口的內(nèi)部類,iterator()方法正是返回了該類的實(shí)例。實(shí)際上該類內(nèi)部定義了多個Iterator的實(shí)現(xiàn)類,用以不同的迭代場景,如下所示:

          /**
           * An optimized version of AbstractList.Itr
           */
          private class Itr implements Iterator<E>{
              ...
          }

          /**
           * An optimized version of AbstractList.ListItr
           */
          private class ListItr extends Itr implements ListIterator<E> {
              ...
          }

          這里以Itr類為例分析hashNext()方法和next()方法具體實(shí)現(xiàn)

          private class Itr implements Iterator<E> {
              // 下一個元素的索引,默認(rèn)值為0
              int cursor;       // index of next element to return
              // 上一個元素的索引
              int lastRet = -1; // index of last element returned; -1 if no such
              int expectedModCount = modCount;
              
              Itr() {}
              
              // 是否有下一個元素
              public boolean hasNext() {
                  // 索引未到達(dá)size處則為true
                  return cursor != size;
              }
              
              // 返回下一個元素(索引從-1開始)
              @SuppressWarnings("unchecked")
              public E next() {
                  // 檢查是否有修改(增加或刪除元素),修改了的話會拋出ConcurrentModificationException
                  // 這里也說明ArrayList類是不支持并發(fā)操作的
                  checkForComodification();
                  
                  // 如果當(dāng)前索引>=size,說明越界了,拋出異常
                  int i = cursor;
                  if (i >= size)
                      throw new NoSuchElementException();
                      
                  // 如果當(dāng)前索引超過數(shù)組長度,說明有其他線程增加元素對數(shù)組進(jìn)行了擴(kuò)容操作
                  // 拋出并發(fā)修改異常
                  Object[] elementData = ArrayList.this.elementData;
                  if (i >= elementData.length)
                      throw new ConcurrentModificationException();
                      
                  // 如果一切正常,索引+1并返回索引位置元素
                  cursor = i + 1;
                  return (E) elementData[lastRet = i];
              }
              
              // 刪除當(dāng)前遍歷到的元素,(至少要調(diào)用一次next()方法)
              public void remove() {
                  // 如果lastRet值<0,說明索引還在-1,一次next()方法都沒調(diào)用,拋出異常
                  if (lastRet < 0)
                      throw new IllegalStateException();
                      
                  // 檢查是否有并發(fā)修改,有修改拋出異常
                  checkForComodification();
                  
                  // 從list中刪除下標(biāo)為lastRet的元素,并更新下標(biāo)索引的值
                  try {
                      ArrayList.this.remove(lastRet);
                      cursor = lastRet;
                      lastRet = -1;
                      expectedModCount = modCount;
                  } catch (IndexOutOfBoundsException ex) {
                      throw new ConcurrentModificationException();
                  }
              }
              ...
          }








          瀏覽 27
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  逼特逼视频最新网址 | 一本道精品在线 | 黄色一级片免费观看 | 色开心五月天 | 台湾无码中文字幕 |