Java Collection框架 - ArrayList

Java Collection框架 - ArrayList

基于jdk1.8

簡(jiǎn)介

ArrayList是基于數(shù)組實(shí)現(xiàn)的動(dòng)態(tài)數(shù)組,不是線程安全

時(shí)間復(fù)雜度:

  • 查找和修改 ==> O(1)
  • 刪除和添加(除了向末尾添加) ==> O(n)

數(shù)據(jù)結(jié)構(gòu)

先看下ArrayList的數(shù)據(jù)結(jié)構(gòu):

ArrayList數(shù)據(jù)結(jié)構(gòu)

ArrayList的數(shù)據(jù)結(jié)構(gòu)很簡(jiǎn)單,就是一個(gè)數(shù)組跟一個(gè)size屬性作為尾指針

主要屬性與方法列表

//代表ArrayList默認(rèn)容量 10
DEFAULT_CAPACITY : int
//內(nèi)部數(shù)組
elementData : Object[]
//容量
size : int
//最大容量 Integer.MAX_VALUE - 8
MAX_ARRAY_SIZE : int

trimToSize() : void
ensureCapacity(int) : void
size() : int
isEmpty() : boolean
contains(Object) : boolean
indexOf(Object) : int
lastIndexOf(Object) : int
clone() : Object
toArray() : Object[]
toArray(T[]) : T[]
get(int) : E
set(int, E) : E
add(E) : boolean
add(int, E) : void
remove(int) : E
remove(Object) : boolean
clear() : void
addAll(Collection<? extends E>) : boolean
addAll(int, Collection<? extends E>) : boolean
removeAll(Collection<?>) : boolean
retainAll(Collection<?>) : boolean
listIterator(int) : ListIterator<E>
listIterator() : ListIterator<E>
subList(int, int) : List<E>
sort(Comparator<? super E>) : void

主要代碼分析

  1. 查找

ArrayList提供了get(int index)用于讀取數(shù)組中的元素。由于ArrayList使用數(shù)組實(shí)現(xiàn),所以可以直接使用下標(biāo)來獲取元素

  public E get(int index) {
      rangeCheck(index);

      return elementData(index);
  }

  E elementData(int index) {
      return (E) elementData[index];
  }
  1. 增加

ArrayList提供了add(E e)、add(int index, E element)、addAll(Collection<? extends E> c)、addAll(int index, Collection<? extends E> c)四個(gè)方法來添加元素

  • add(E e):將元素添加到ArrayList末尾

      public boolean add(E e) {
          ensureCapacityInternal(size + 1);  // Increments modCount!!
          elementData[size++] = e;
          return true;
      }
    

    這里的ensureCapacityInternal()方法的作用是嘗試對(duì)數(shù)組進(jìn)行擴(kuò)容

  • add(int index, E element):將元素添加至指定位置,并將之后的元素向后移一位

      public void add(int index, E element) {
          //判斷索引位置是否合法
          rangeCheckForAdd(index);
    
          ensureCapacityInternal(size + 1);  // Increments modCount!!
          //將原數(shù)組從index之后的元素,全部向后移動(dòng)一位
          //其目的是將index位置空出來
          System.arraycopy(elementData, index, elementData, index + 1,
                           size - index);
          elementData[index] = element;
          size++;
      }
    

    這里的rangeCheckForAdd(index)方法的作用是檢查index的值是否在0~size之間。ensureCapacityInternal()方法的作用與上面一樣,都是嘗試對(duì)數(shù)組進(jìn)行擴(kuò)容。System.arraycopy()方法用于將elementDataindex開始的元素復(fù)制到elementDataindex+1位置,一共復(fù)制size-index個(gè)元素,目的是將index位置空出來,方便后來的重新賦值

  • addAll(Collection<? extends E> c):將集合內(nèi)的所有元素依次添加到ArrayList末尾

      public boolean addAll(Collection<? extends E> c) {
          Object[] a = c.toArray();
          int numNew = a.length;
          ensureCapacityInternal(size + numNew);  // Increments modCount
          //將a數(shù)組內(nèi)的元素添加到內(nèi)部數(shù)組中
          System.arraycopy(a, 0, elementData, size, numNew);
          size += numNew;
          return numNew != 0;
      }
    

    這里的System.arraycopy()方法用于將a數(shù)組從0開始的所有元素復(fù)制到elementDatasize位置,一共復(fù)制numNew個(gè),使用System.arraycopy()方法相比于一個(gè)個(gè)復(fù)制速度更快

  • addAll(int index, Collection<? extends E> c):將集合內(nèi)所有元素依次添加到指定位置

      public boolean addAll(int index, Collection<? extends E> c) {
          //確定索引位置是否合法
          rangeCheckForAdd(index);
    
          Object[] a = c.toArray();
          int numNew = a.length;
          ensureCapacityInternal(size + numNew);  // Increments modCount
    
          //計(jì)算需要后移的元素個(gè)數(shù)
          int numMoved = size - index;
          //如果大于0,就將指定元素后移
          if (numMoved > 0)
              System.arraycopy(elementData, index, elementData, index + numNew,
                               numMoved);
    
          System.arraycopy(a, 0, elementData, index, numNew);
          size += numNew;
          return numNew != 0;
      }
    

    在這里System.arraycopy()的作用是先將指定元素后移numNew位(如果有必要),然后將a數(shù)組中的元素復(fù)制到指定位置

  1. 修改

ArrayList提供了set(int index, E element)方法來修改指定位置的元素

  • set(int index, E element):將index位置的元素設(shè)置為element

      public E set(int index, E element) {
          rangeCheck(index);
    
          E oldValue = elementData(index);
          elementData[index] = element;
          return oldValue;
      }
    

    add(int index, E element)方法類似,只不過不需要后移元素了

  1. 刪除

ArrayList提供了remove(int index)、remove(Object o)、removeAll(Collection<?> c)、retainAll(Collection<?> c)四個(gè)方法來刪除元素

  • remove(int index):刪除指定位置的元素

      public E remove(int index) {
          rangeCheck(index);
    
          modCount++;
          //獲取指定位置的元素
          E oldValue = elementData(index);
    
          //計(jì)算需要移動(dòng)的元素個(gè)數(shù)
          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;
      }
    
  • remove(Object o):刪除指定元素

      public boolean remove(Object o) {
          if (o == null) { //刪除元素為null的情況
              for (int index = 0; index < size; index++)
                  if (elementData[index] == null) {
                      fastRemove(index);
                      return true;
                  }
          } else { //不為null
              for (int index = 0; index < size; index++)
                  if (o.equals(elementData[index])) {
                      fastRemove(index);
                      return true;
                  }
          }
          return false;
      }
    
      private void fastRemove(int index) {
          modCount++;
          int numMoved = size - index - 1;
          if (numMoved > 0)
              System.arraycopy(elementData, index+1, elementData, index,
                               numMoved);
          //將最后的多余元素清除,防止內(nèi)存泄漏
          elementData[--size] = null; // clear to let GC do its work
      }
    
  • removeAll(Collection<?> c):刪除所有集合中包含的元素

      public boolean removeAll(Collection<?> c) {
          Objects.requireNonNull(c);
          return batchRemove(c, false);
      }
    
      private boolean batchRemove(Collection<?> c, boolean complement) {
          final Object[] elementData = this.elementData;
          int r = 0, w = 0;
          boolean modified = false;
          try {
              for (; r < size; r++)
                  //保留元素策略取決于complement參數(shù)
                  //用于實(shí)現(xiàn)removeAll跟retainAll方法
                  if (c.contains(elementData[r]) == complement)
                      //將保留元素移動(dòng)到列表頭部
                      elementData[w++] = elementData[r];
          } finally {
              // Preserve behavioral compatibility with AbstractCollection,
              // even if c.contains() throws.
    
              //當(dāng)contains方法拋出異常時(shí),r不會(huì)等于size
              if (r != size) {
                  //0~w:需要保留
                  //w~r:需要?jiǎng)h除
                  //r~size:不能確定
                  //只刪除必定需要?jiǎng)h除的元素
                  System.arraycopy(elementData, r,
                                   elementData, w,
                                   size - r);
                  w += size - r;
              }
              if (w != size) {
                  // clear to let GC do its work
                  for (int i = w; i < size; i++)
                      elementData[i] = null;
                  modCount += size - w;
                  size = w;
                  modified = true;
              }
          }
          return modified;
      }
    

    removeAll()方法依賴于batchRemove()方法,通過complement屬性指定需要?jiǎng)h除的元素為c集合包含的元素

  • retainAll(Collection<?> c):刪除所有集合中不包含的元素

      public boolean retainAll(Collection<?> c) {
          Objects.requireNonNull(c);
          return batchRemove(c, true);
      }
    

    removeAll()方法類似,通過complement屬性指定需要?jiǎng)h除的元素為c集合不包含的元素

  1. 擴(kuò)增

ArrayList提供了ensureCapacity(int minCapacity)、ensureCapacityInternal(int minCapacity)兩個(gè)方法供其他方法調(diào)用,用于擴(kuò)增容量。這兩個(gè)方法都依賴ensureExplicitCapacity(int minCapacity)方法,ensureExplicitCapacity(int minCapacity)方法又依賴grow(int minCapacity)方法

  public void ensureCapacity(int minCapacity) {
      int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
          // any size if not default element table
          ? 0
          // larger than default for default empty table. It's already
          // supposed to be at default size.
          : DEFAULT_CAPACITY;

      if (minCapacity > minExpand) {
          ensureExplicitCapacity(minCapacity);
      }
  }

  private void ensureCapacityInternal(int minCapacity) {
      if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
          minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
      }

      ensureExplicitCapacity(minCapacity);
  }

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

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

  private void grow(int minCapacity) {
      // overflow-conscious code
      int oldCapacity = elementData.length;
      //計(jì)算新的數(shù)組容量,以1.5倍擴(kuò)容
      //1.5倍是通過測(cè)試得到的一個(gè)最佳值
      //以1.5倍擴(kuò)容。既不會(huì)消耗太多性能,也不會(huì)消耗太多內(nèi)存
      int newCapacity = oldCapacity + (oldCapacity >> 1);
      if (newCapacity - minCapacity < 0)
          newCapacity = minCapacity;
      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);
  }
  1. 迭代器

ArrayList實(shí)現(xiàn)了Itr、ListItr兩個(gè)迭代器。ArrayList的迭代器能在遍歷的同時(shí)添加或刪除元素,是由于在這兩個(gè)方法中修改了迭代器的expectedModCount記錄

  public void remove() {
      if (lastRet < 0)
          throw new IllegalStateException();
      checkForComodification();

      try {
          ArrayList.this.remove(lastRet);
          cursor = lastRet;
          lastRet = -1;
          //修改記錄
          expectedModCount = modCount;
      } catch (IndexOutOfBoundsException ex) {
          throw new ConcurrentModificationException();
      }
  }

  public void add(E e) {
      checkForComodification();

      try {
          int i = cursor;
          ArrayList.this.add(i, e);
          cursor = i + 1;
          lastRet = -1;
          //修改記錄
          expectedModCount = modCount;
      } catch (IndexOutOfBoundsException ex) {
          throw new ConcurrentModificationException();
      }
  }

總結(jié)

  1. ArrayList的默認(rèn)初始容量為10
  2. ArrayList以1.5倍擴(kuò)容
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容