从底层源码认知Arraylist

很多小伙伴都知道Arraylist和Linkedlist相比,查询快,增删慢,这是为什么呢?它底层又是怎样的结构呢?请随我一起从源码分析(如有错误请大佬指出,本人是几个月小白)

简介

java容器集合可分为key值存储(Collection)和key-value存储(Map)两类,如下图继承链中ArrayList则是继承AbstractList类,实现了Collection接口
在这里插入图片描述

特点

对于ArrayList主要特点:
基于Object数组实现的List集合
不像数组一样长度固定,ArrayList可以实现动态扩容
有序,可重复
线程非安全,效率高
基于数组,查询快,但删除慢
元素可以为null

接下来源码分析(JDK1.8)

继承关系

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

可见ArrayList支持泛型,且继承AbstractList类,实现了List,RandomAccess, Cloneable, java.io.Serializable接口,除开List,其他三个仅仅标识ArrayList可以具有随机访问,克隆,序列化功能

例如Cloneable仅仅是一个接口里面没有内容,仅为标识作用,其他两个一样

public interface Cloneable {
}

成员变量

private static final long serialVersionUID = 8683452581122892189L;

    // 默认初始容量为10
    private static final int DEFAULT_CAPACITY = 10;
     // 初始一个空的Object数组
    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.
     * (有道翻译)用于默认大小的空实例的共享空数组实例。我们将其与空的ELEMENTDATA区分开来,
     * 以便知道添加第一个元素时应该膨胀多少。
     * 后面默认构造函数将用到DEFAULTCAPACITY_EMPTY_ELEMENTDATA为elementData初始化
     */
    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关键字:大致上是,在实现Serilizable接口后
     * 将不需要序列化的属性前添加关键字transient,
     * 序列化对象的时候,这个属性就不会序列化到指定的目的地中。
     */
    transient Object[] elementData; // 非私有简化嵌套类访问

    //ArrayList的大小(包含的元素数量),默认为0。
    private int size;

构造方法三个

    /**
     * 构造一个指定初始容量的Arraylist
     */
  public ArrayList(int initialCapacity) {
  			//如果大于0则初始化elementData数组
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
            //如果初始容量为0,则创建空数组
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
        	//小于0则抛出异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    /**
     * 构造一个初始容量为10的空列表。
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     * (有道翻译)构造一个包含指定集合元素的列表,按照集合的迭代器返回元素的顺序。
     *
     * @param c将其元素放置到列表中的集合
     * @throws 如果指定的集合为空,则抛出NullPointerException
     */
    public ArrayList(Collection<? extends E> c) {
    	//将c转换为数组
        elementData = c.toArray();
    
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            //c.toArray可能(不正确)不返回Object[](参见6260652)
            //如果不正确,所以下面有判断
            if (elementData.getClass() != Object[].class)
            	//使用Arrays.copyOf将elementData拷贝成Object数组
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // 长度为0则替换为空数组
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

常用的增删改查

add方法

add方法主要有2个

    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
    	//先调用ensureCapacityInternal判断是否需要扩容
        ensureCapacityInternal(size + 1);  // 增加 modCount!!
        //将元素e存入elementData数组并且size++
        elementData[size++] = e;
        return true;
    }

    /**
     * Inserts the specified element at the specified position in this
     * list. Shifts the element currently at that position (if any) and
     * any subsequent elements to the right (adds one to their indices).
     *
     * @param index index at which the specified element is to be inserted
     * @param element element to be inserted
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public void add(int index, E element) {
    	//这里因为是在指定索引位置添加元素,则需要调用rangeCheckForAdd检查索引位置
        rangeCheckForAdd(index);
		//再确保容量足够
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //调优arraycopy方法,将index插入元素的位置的后面元素依次后移,所以插入元素是非常耗时
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        //将element元素放入index索引处
        elementData[index] = element;
        size++;
    }
ensureCapacityInternal

//上面两个都调用了ensureCapacityInternal和rangeCheckForAdd方法

    /**
     * Increases the capacity of this <tt>ArrayList</tt> instance, if
     * necessary, to ensure that it can hold at least the number of elements
     * specified by the minimum capacity argument.
     *
     * @param   minCapacity   the desired minimum capacity
     */
    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);
        }
    }
	//参数为minCapability=size+1,
    private void ensureCapacityInternal(int minCapacity) {
    	//判断elementData数组是否为空数组,空的话则返回默认为10的长度和size+1的最大值,
    	//之前size初始化默认为0,如果这是第一次则扩容为10,因为10>1;
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
		
        ensureExplicitCapacity(minCapacity);
    }
	
    private void ensureExplicitCapacity(int minCapacity) {
    	//modCount+1;
        modCount++;

        // 这里判断需不需要扩容,minCapacity 为10,而现在length为0,还没添加元素,
        
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

//要分配的数组的最大大小。一些虚拟机在数组中保留一些头字。
//尝试分配更大的数组可能会导致OutOfMemoryError:请求的数组大小超过VM限制
//这里设置 MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * 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) {
        // 记载length 
        int oldCapacity = elementData.length;
        //设置新容量为oldCapacity 的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //下面就是判断扩容后的长度和之前的长度,这里<0意思第一次没有元素newCapacity 为0;
        //则将默认的10赋予新容量
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //如果大于MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
        //则调用hugeCapacity方法赋予newCapacity 值		        
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        //minCapacity通常接近于size,所以这是一个胜利
        //最后调用Arrays.copyOf得到新的容量为newCapacity的数组
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
            //返回如果大于最大允许的长度,则返回 Integer.MAX_VALUE 尽量将最大的容量返回
            //否则返回 MAX_ARRAY_SIZE
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

    /**
     * Returns the number of elements in this list.
     *
     * @return the number of elements in this list
     */
rangeCheckForAdd
//判断是否越界
    private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

get方法set方法


     //这个比较简单,直接调用rangeCheck检查索引,然后返回
    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }

    public E set(int index, E element) {
    	//一样检查索引
        rangeCheck(index);
		//保存之前index上的元素
        E oldValue = elementData(index);
        //覆盖并将之前的元素返回
        elementData[index] = element;
        return oldValue;
    }

remove方法

  //首先检查索引,然后modcount++,这里modcount也许有人不是很了解,这里只大致说一下
  //modCount 顾名思义就是修改次数,在迭代器中有一个属性expectedModCount;
  //在迭代器初始化过程中会将这个值赋给迭代器的 expectedModCount。
  //在迭代过程中,判断 modCount 跟 expectedModCount 是否相等,不相等则代表其他线程对这容器进行了修改,这里还有个Fail-Fast 机制,没了解的同学建议了解一下
    public E remove(int index) {
        rangeCheck(index);

        modCount++;
        //记录删除的元素
        E oldValue = elementData(index);
		//记录将移动的元素个数
        int numMoved = size - index - 1;
        //依次向前移动,这里又可以证明Arraylist删除非常的麻烦
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
         //最后一个元素只为空,这样该元素就没被引用,而数组元素是放值的堆中,会被GC管理回收
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

//通过传入一个元素,判断时候有则删除,且返回true,否则返回false
    public boolean remove(Object o) {
    //o==null,循环遍历,有则调用fastRemove
        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++)
                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
    }

    //清空数组,全部只为空
    public void clear() {
        modCount++;

        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }

其他常用方法

如下代码很好理解不再多说


  public boolean isEmpty() {
        return size == 0;
    }

    public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

    public int lastIndexOf(Object o) {
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

总结

可以从上面的源码看出,ArrayList底层是一个Object类型的数组,可以存储基本类型和引用类型,元素可以为空,因为是基于数组,具有索引,所以查询效率高,但是面对增删改时候,会依次移动元素导致效率下降,浪费时间
为什么非线程安全:上面在做add增加的时候,会进行扩容,在最后的copyOf方法的时候会返回elementData对象,如果有多个线程则会返回多个elementData对象,这时候后来的线程返回的elementData对象将会把之前的线程elementData对象覆盖,这里想要线程安全可以使用下面三个类
java.util.Collections.SynchronizedList
vector
java.util.concurrent.CopyOnWriteArrayList

好了,以上是个人总结的部分源码,有兴趣的小伙伴建议还是打开电脑看看源码,谢谢观看。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值