Java Collection框架 - ArrayList

Java Collection框架 - ArrayList

基于jdk1.8

简介

ArrayList是基于数组实现的动态数组,不是线程安全

时间复杂度:
* 查找和修改 ==> O(1)
* 删除和添加(除了向末尾添加) ==> O(n)

数据结构

先看下ArrayList的数据结构:

ArrayList数据结构

ArrayList的数据结构很简单,就是一个数组跟一个size属性作为尾指针

主要属性与方法列表

//代表ArrayList默认容量 10
DEFAULT_CAPACITY : int
//内部数组
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)用于读取数组中的元素。由于ArrayList使用数组实现,所以可以直接使用下标来获取元素

    public E get(int index) {
        rangeCheck(index);
    
        return elementData(index);
    }
    
    E elementData(int index) {
        return (E) elementData[index];
    }
  2. 增加

    ArrayList提供了add(E e)add(int index, E element)addAll(Collection<? extends E> c)addAll(int index, Collection<? extends E> c)四个方法来添加元素

    • add(E e):将元素添加到ArrayList末尾
      public boolean add(E e) {
          ensureCapacityInternal(size + 1);  // Increments modCount!!
          elementData[size++] = e;
          return true;
      }

    这里的ensureCapacityInternal()方法的作用是尝试对数组进行扩容

    • add(int index, E element):将元素添加至指定位置,并将之后的元素向后移一位
      public void add(int index, E element) {
          //判断索引位置是否合法
          rangeCheckForAdd(index);
    
          ensureCapacityInternal(size + 1);  // Increments modCount!!
          //将原数组从index之后的元素,全部向后移动一位
          //其目的是将index位置空出来
          System.arraycopy(elementData, index, elementData, index + 1,
                           size - index);
          elementData[index] = element;
          size++;
      }

    这里的rangeCheckForAdd(index)方法的作用是检查index的值是否在0~size之间。ensureCapacityInternal()方法的作用与上面一样,都是尝试对数组进行扩容。System.arraycopy()方法用于将elementDataindex开始的元素复制到elementDataindex+1位置,一共复制size-index个元素,目的是将index位置空出来,方便后来的重新赋值

    • addAll(Collection<? extends E> c):将集合内的所有元素依次添加到ArrayList末尾
      public boolean addAll(Collection<? extends E> c) {
          Object[] a = c.toArray();
          int numNew = a.length;
          ensureCapacityInternal(size + numNew);  // Increments modCount
          //将a数组内的元素添加到内部数组中
          System.arraycopy(a, 0, elementData, size, numNew);
          size += numNew;
          return numNew != 0;
      }

    这里的System.arraycopy()方法用于将a数组从0开始的所有元素复制到elementDatasize位置,一共复制numNew个,使用System.arraycopy()方法相比于一个个复制速度更快

    • addAll(int index, Collection<? extends E> c):将集合内所有元素依次添加到指定位置
      public boolean addAll(int index, Collection<? extends E> c) {
          //确定索引位置是否合法
          rangeCheckForAdd(index);
    
          Object[] a = c.toArray();
          int numNew = a.length;
          ensureCapacityInternal(size + numNew);  // Increments modCount
    
          //计算需要后移的元素个数
          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数组中的元素复制到指定位置

  3. 修改

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

    • set(int index, E element):将index位置的元素设置为element
      public E set(int index, E element) {
          rangeCheck(index);
    
          E oldValue = elementData(index);
          elementData[index] = element;
          return oldValue;
      }

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

  4. 删除

    ArrayList提供了remove(int index)remove(Object o)removeAll(Collection<?> c)retainAll(Collection<?> c)四个方法来删除元素

    • remove(int index):删除指定位置的元素
      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;
      }
    • 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);
          //将最后的多余元素清除,防止内存泄漏
          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参数
                  //用于实现removeAll跟retainAll方法
                  if (c.contains(elementData[r]) == complement)
                      //将保留元素移动到列表头部
                      elementData[w++] = elementData[r];
          } finally {
              // Preserve behavioral compatibility with AbstractCollection,
              // even if c.contains() throws.
    
              //当contains方法抛出异常时,r不会等于size
              if (r != size) {
                  //0~w:需要保留
                  //w~r:需要删除
                  //r~size:不能确定
                  //只删除必定需要删除的元素
                  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属性指定需要删除的元素为c集合包含的元素

    • retainAll(Collection<?> c):删除所有集合中不包含的元素
      public boolean retainAll(Collection<?> c) {
          Objects.requireNonNull(c);
          return batchRemove(c, true);
      }

    removeAll()方法类似,通过complement属性指定需要删除的元素为c集合不包含的元素

  5. 扩增

    ArrayList提供了ensureCapacity(int minCapacity)ensureCapacityInternal(int minCapacity)两个方法供其他方法调用,用于扩增容量。这两个方法都依赖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;
        //计算新的数组容量,以1.5倍扩容
        //1.5倍是通过测试得到的一个最佳值
        //以1.5倍扩容。既不会消耗太多性能,也不会消耗太多内存
        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);
    }
  6. 迭代器

    ArrayList实现了ItrListItr两个迭代器。ArrayList的迭代器能在遍历的同时添加或删除元素,是由于在这两个方法中修改了迭代器的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();
        }
    }

总结

  1. ArrayList的默认初始容量为10
  2. ArrayList以1.5倍扩容
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值