Arraylist原理及源码分析

闲话

以前学习从来不看源码 ,一是怕麻烦,二是看不懂,到现在这些简单问题还是不清楚,只能被面试官吊打了,今天就拿Arralist先开刀。

一、ArrayList的数据结构

ArrayList的底层数据结构就是一个数组,数组元素的类型为Object类型,对ArrayList的所有操作底层都是基于数组的。

二、ArrayList的线程安全性

对ArrayList进行添加元素的操作的时候是分两个步骤进行的,即第一步先在object[size]的位置上存放需要添加的元素;第二步将size的值增加1。由于这个过程在多线程的环境下是不能保证具有原子性的,因此ArrayList在多线程的环境下是线程不安全的。

具体举例说明:在单线程运行的情况下,如果Size = 0,添加一个元素后,此元素在位置 0,而且Size=1;而如果是在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B也向此ArrayList 添加元素,因为此时 Size 仍然等于 0 (注意哦,我们假设的是添加一个元素是要两个步骤哦,而线程A仅仅完成了步骤1),所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,都增 加 Size 的值。  那好,现在我们来看看 ArrayList 的情况,元素实际上只有一个,存放在位置 0,而Size却等于 2。这就是“线程不安全”了。

如果非要在多线程的环境下使用ArrayList,就需要保证它的线程安全性,通常有两种解决办法:第一,使用synchronized关键字;第二,可以用Collections类中的静态方法synchronizedList();对ArrayList进行调用即可。

三、ArrayList的继承关系

ArrayList继承AbstractList抽象父类,实现了List接口(规定了List的操作规范)、RandomAccess(可随机访问)、Cloneable(可拷贝)、Serializable(可序列化)。

四、源码分析

1. 属性分析

      //显示指定序列化id 之前解释过
       private static final long serialVersionUID = 8683452581122892189L;

     //初始默认容量为10
    private static final int DEFAULT_CAPACITY = 10;


     // 如果构造方法传入的capacity=0 就会返回这个 下面构造方法可以看到
    private static final Object[] EMPTY_ELEMENTDATA = {};

      //空参默认是这个
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

      //Object数组
    transient Object[] elementData; // non-private to simplify nested class access

      //数组长度
    private int size;
      
      //数组最大容量
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

2. 构造方法分析

三个构造方法 ,如下:

    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            //就创建容量为initialCapacity数组
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            //返回EMPTY_ELEMENTDATA
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            //长度不合法异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }



public ArrayList() {
             //返回EMPTY_ELEMENTDATA
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }



//传入一个collection参数
  public ArrayList(Collection<? extends E> c) {
      //把传入的集合转化成数组并赋给elementData
        elementData = c.toArray();
      //如果长度不为0
        if ((size = elementData.length) != 0) {
          //如果elementData类型不是Object
            if (elementData.getClass() != Object[].class)
                //把elementData数组改拷贝为Objectp[]
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            //空数组
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

3. 主要方法

      // 1)把容量减到实际长度
    public void trimToSize() {
      //修改次数增加
        modCount++;
      //如果列表长度小于容量
        if (size < elementData.length) {
            elementData = (size == 0)
                  //{}
              ? EMPTY_ELEMENTDATA  
                  //长度缩减至size实际容量
              : Arrays.copyOf(elementData, size);
        }
    }




      // 2)检查容量
    public void ensureCapacity(int minCapacity) {
      //传入的minCapacity大于数组容量 并且数组不为{} ,minCapacity大于默认容量
        if (minCapacity > elementData.length
            && !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
                 && minCapacity <= DEFAULT_CAPACITY)) {
            modCount++;
            //扩容
            grow(minCapacity);
        }
    }

      // 3)扩容方法
   private Object[] grow(int minCapacity) {
      //原数组拷贝到新数组 新数组容量调用newCapacity方法
        return elementData = Arrays.copyOf(elementData,
                                           newCapacity(minCapacity));
    }

      private int newCapacity(int minCapacity) {
           //旧长度
        int oldCapacity = elementData.length;
            //旧长度+旧长度右移1位 即为原来的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
      //如果新长度小于等于minCapacity
        if (newCapacity - minCapacity <= 0) {
            //如果数组为DEFAULTCAPACITY_EMPTY_ELEMENTDATA
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            //返回他两中的最大值
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            if (minCapacity < 0) // overflow
                throw new OutOfMemoryError();
            return minCapacity;
        }
        //小于 MAX_ARRAY_SIZE  返回newCapacity 否则调用hugeCapacity返回最大值
        return (newCapacity - MAX_ARRAY_SIZE <= 0)
            ? newCapacity
            : hugeCapacity(minCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE)
            ? Integer.MAX_VALUE
            : MAX_ARRAY_SIZE;
    }



      // 4)获取元素方法
       public E get(int index) {
            //检查是否越界
        rangeCheck(index);

        return elementData(index);
    }
           //检查是否越界
        private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

// 5)添加元素方法
   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;
        size++;
    }


// 6)删除元素
  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;
    }
posted @ 2020-05-19 14:55  lanvce  阅读(202)  评论(0)    收藏  举报
//小火箭