ArrayList原理分析及手写ArrayList

       在面试过程中,会遇到ArrayList实现原理是怎么样,在什么情况会对集合进行扩容等问题;ArrayList相对于其它的集合是比较简单的了。在后面会将手写的ArrayList代码附上,在这之前我们需要了解ArrayList一些主要的成员变量以及原理。

1、主要的成员变量解释

   /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    private int size;
    /**
     * The maximum size of array to allocate.
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
  • DEFAULT_CAPACITY :集合默认的初始大小
  • EMPTY_ELEMENTDATA: 默认一个空的数组对象;在调用有参的ArrayList时会使用的,比如new ArrayList(int initialCapacity)时,initialCapacity等于0的时候,就会赋值给elementData了。
  • DEFAULTCAPACITY_EMPTY_ELEMENTDATA :默认一个空的数组对象,从使用程度来看,主要是区分在集合实例化的方式不一样,在使用默认无参的构造方法时,会将DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋值给elementData;这个跟EMPTY_ELEMENTDATA都是一个空的数组对象,不是很明白为什么要搞两个空的数组对象
  • elementData :元素存放的位置;从这里可以看出ArrayList实际是用数组存储元素的
  • size :集合存放的元素大小

2、集合扩容原理

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

这是大家熟悉的ArrayList对象中的add方法;在这个方法中会分一下步骤来进行:

 

  • 计算当前最新的容量;会拿当前即将要添加的元素下标去ensureCapacityInternal(minCapacity)方法中做数组扩容判断。我们去看跟数组扩容相关的方法,不必要的代码就直接忽略
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        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);
    }

在ensureExplicitCapacity方法中"minCapacity - elementData.length"这段代码才是扩容的条件。当计算出来的最新下标值减去当前数组的长度大于0时,就满足扩容条件了。

而在grow方法中,就是进行扩容的逻辑int newCapacity = oldCapacity + (oldCapacity >> 1);主要代码,这一段代码就是决定数组要扩容到多少,简单来说就是:当前的数组长度+(当前数组长度右移1位);结合前面说的DEFAULT_CAPACITY变量,集合默认的数组大小是10,对号入座之后应该是:int newCapacity = 10 + (10 >> 1) 最终结果是15,最后通过Arrays.copyOf对数组进行拷贝扩容。在Arrays.copyOf方法中底层实际是调用管理System.arraycopy方法的,这里不继续讲解,就当是额外的话题。最终将扩容后的数组赋值给elementData,最后通过elementData[size++] = e;将元素存放到数组中。

总结:ArrayList实际是用数组存储元素,这也决定的了对随机获取元素时,效率是很高的;但是ArrayList对元素的增删改性能损耗是比较大的因为增加或者删除元素,都需要移动集合里面的元素,因此ArrayList不适合频繁的增删改操作,比较适合元素的获取。

以上就是对ArrayList扩容的讲解,其它的方法也相对简单,这里就直接忽略了,有兴趣的同学可以自行去研究。接下来就是依葫芦画瓢,简单自己实现一个ArrayList的代码编写了。


3、手写ArrayList

import java.util.Arrays;

/**
 * @Description: 自定义ArrayList
 * @CreatedDate: 2018/12/10 15:17
 * @Author: 
 */
public class CustomArrayList<E> {
     /**
      * 初始容量大小
      */
     private static final int DEFAULT_CAPACITY = 10;
     /**
      * 默认一个空的数组
      */
     private static final Object[] DEFAULT_EMPTY_ELEMENTDATA = {};
     /**
      * 数组最大的一个临界值
      */
     private static final int MAX_SIZE = Integer.MAX_VALUE - 8;
     /**
      * 实际存放的元素值
      */
     private Object[] elementData;
     /**
      * 集合大小
      */
     private int size;

     public CustomArrayList() {
          this.elementData = DEFAULT_EMPTY_ELEMENTDATA;
     }

     /**
      * @Description: 添加元素
      * @Author: 
      * @CreatedDate: 2018/12/10 15:58
      * @param
      * @return
      */
     public boolean add(E e) {
          //计算容量,是否需要扩容
          checkExpansionCapacity(size + 1);
          elementData[size++] = e;
          return true;
     }
     /**
      * @Description: 获取集合元素:获取元素之前,先检查是否会出现数组下标越界
      * @Author: 
      * @CreatedDate: 2018/12/10 16:57
      * @param
      * @return
      */
     public E get(int index) {
          rangeCheck(index);
          return elementData(index);
     }
     /**
      * @Description: 根据下标删除元素
      * @Author: 
      * @CreatedDate: 2018/12/10 16:57
      * @param
      * @return
      */
     public E remove(int index) {
          rangeCheck(index);
          E oldValue = elementData(index);
          resetEmpty(index);
          return oldValue;
     }
     /**
      * @Description: 根据对象删除元素
      * @Author: 
      * @CreatedDate: 2018/12/10 18:10
      * @param
      * @return
      */
     public boolean remove(E e) {
          if (e == null) {
               //null,则查询是否有null的数据,有则删除
               for (int i =0 ;i <elementData.length; i++) {
                    if (elementData[i] == null) {
                         resetEmpty(i);
                         return true;
                    }
               }
          }else{
               for (int i =0 ;i <elementData.length; i++) {
                    if (e.equals(elementData[i])) {
                         resetEmpty(i);
                         return true;
                    }
               }
          }

          return false;
     }
     /**
      * @Description: 集合大小
      * @Author: 
      * @CreatedDate: 2018/12/10 16:57
      * @param
      * @return
      */
     public int size(){
          return this.size;
     }
     /**
      * @Description: 删除某一个元素 or 将某一个元素置空
      * System.arraycopy方法参数解释:
      *    Object src : 原数组
      *    int srcPos : 从元数据的起始位置开始
      *   Object dest : 目标数组
      *   int destPos : 目标数组的开始起始位置
      *   int length  : 要copy的数组的长度
      *
      * @Author: 
      * @CreatedDate: 2018/12/10 18:17
      * @param
      * @return
      */
     private void resetEmpty(int index){
          int movedIndex = size - index - 1;
          //利用底层提供的数组拷贝api来实现拷贝功能
          if (movedIndex > 0)
               System.arraycopy(elementData, index + 1, elementData,  index, movedIndex);
          elementData[--size] = null;
     }

     /**
      * @Description: 获取数组中元素
      * @Author: 
      * @CreatedDate: 2018/12/10 16:57
      * @param
      * @return
      */
     private E elementData(int index) {
          return (E) this.elementData[index];
     }
     /**
      * @Description: 范围检查
      * @Author: 
      * @CreatedDate: 2018/12/10 16:37
      * @param
      * @return
      */
     private void rangeCheck(int index) {
          if (index >= size)
               throw new IndexOutOfBoundsException("index:" + index + ", size:" + size);
     }

     /**
      * @Description: 检查数组是否需要扩容
      * @Author: 
      * @CreatedDate: 2018/12/10 16:05
      * @param
      * @return
      */
     private void checkExpansionCapacity(int minCapacity){
          int capacity = calculateCapacity(elementData, minCapacity);
          if (capacity - elementData.length > 0)
               expansionCapacity(capacity);
     }
     /**
      * @Description: 扩容数组:
      * @Author: 
      * @CreatedDate: 2018/12/10 16:05
      * @param
      * @return
      */
     private void expansionCapacity(int minCapacity){
          int oldCapacity = elementData.length;
          int newCapacity = oldCapacity + (oldCapacity >> 1);
          if (newCapacity - minCapacity < 0)
               newCapacity = minCapacity;
          if (newCapacity - MAX_SIZE > 0)
               newCapacity = MAX_SIZE;
          //进行数组扩容
          elementData = Arrays.copyOf(elementData, newCapacity);
     }
     /**
      * @Description: 计算容量
      * @Author: 
      * @CreatedDate: 2018/12/10 15:46
      * @param
      * @return
      */
     private int calculateCapacity(Object[] elementData, int minCapacity){
          if (elementData.getClass() == DEFAULT_EMPTY_ELEMENTDATA.getClass()) {
               //一开始数组是空的,则计算出最大的容量值
               return Math.max(DEFAULT_CAPACITY, minCapacity);
          }
          return minCapacity;
     }

}


public class ArrayListDemo {

     public static void main(String[] args) {
          CustomArrayList<String> list = new CustomArrayList<String>();
          list.add("test1");
          list.add("test2");
          list.add("test3");
          list.add("test4");
          list.add("test5");
          for (int i= 0; i< 8; i ++ ) {
               if(i == 5) {
                    System.out.println("start...");
               }
               list.add("demo_" + i);
          }
          System.out.println(list.size());
          list.remove(0);
          list.remove("test2");
          String result = list.get(1);
          System.out.println(result);
          System.out.println(list.size());
     }

}

以上内容如有问题,希望可以留言给我,我可以及时纠正。很高兴与您相遇,希望对阅读完后对您有所帮助;

学习永无止境,不进则退~!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章