ArrayList

ArrayList

​ 著作:lss 路漫漫其修远兮,可不至于代码。

简介

​ ArrayList 实现了 List 接口是一个顺序容器,底层是由数组实现,允许放入 null 元素。简之:有序,有下标。该类并没有同步机制。存在线程安全的问题。

​ 以下代码是基于 jdk1.8 源码所写。

构造方法

​ 在看构造方法之前,先考虑一个问题。 ArrayList 的默认容量 (capacity) 是多少呢? 为什么这么说,因为很多小伙伴在谈及这个问题的时候都会不假思索的说是 10。那到底是多少呢 ? 请带着这个疑问往下走。

ArrayList( int initialCapacity )


private static final Object[] EMPTY_ELEMENTDATA = {};

public ArrayList(int initialCapacity) { 
        if (initialCapacity > 0) {
            // ArrayList 的容量为 initialCapacity
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            // ArrayList 的容量为 0 
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

ArrayList ( )

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; 

public ArrayList() {
    	// ArrayList 的容量为 0
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

ArrayList ( Collection<? extends E> c)

private static final Object[] EMPTY_ELEMENTDATA = {};

public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // 当传的集合不为 null  将拷贝到一个新的集合
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // 初始容量为 0 
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

​ 看完里面的构造方法后,emmmm… 10呢??? 其实在创建 ArrayList 的时候,默认长度是0。解决了上一个疑惑以后, 请进行思考第二个问题。 大家都说初始容量是 10 。构造方面里面明明没有10 。那么这个是 10 的传说是如何来的 ? 带着疑惑继续往下看。

添加方法 add

add(E e)

// 默认下的 数组实列
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// ArrayList 的元数据
transient Object[] elementData;
// 默认的初始容量
private static final int DEFAULT_CAPACITY = 10;
// ArrayList 的长度
private int size;

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;


public boolean add(E e) {
    	// 为了确保 ArrayList 有足够容量
        ensureCapacityInternal(size + 1);  
    	// 存储数据
        elementData[size++] = e;
        return true;
    }

private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            // 在 默认的容量数据中 和 minCapacity 取最大值
			
            /**   10 是这个时候来的。假设我这次是第一次添加。此时 elementData 是 空数组
              *  此时 minCapacity = Math.max( 10, size +1 )  
              */
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
     	// 明确 ArrayList 的容量
        ensureExplicitCapacity(minCapacity);
    }

private void ensureExplicitCapacity(int minCapacity) {
     	// 操作记录  用于并发写的情况下
        modCount++;
     	// overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
	

private void grow(int minCapacity) {
        // 获取 ArrayList 的长度
        int oldCapacity = elementData.length;
    	// 获取新的 ArrayList 的长度
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            // 保持原由长度
            newCapacity = minCapacity;
    	// 新的长度 > Integer.MAX.VALUE - 8
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            /**
              *    解析 为什么 ArrayList 的最大容量是 Integer.MAX.VALUE - 8 而不是 Integer.MAX.VALUE
              *    因为 ArrayList是由数组实现,数组是需要 8 bytes 存储自己的大小。
              */
            newCapacity = hugeCapacity(minCapacity);
        // 扩容
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

​ 看完这段添加方法, 才看到了那个传说中的 10 。 也就是说其实数组默认的初始容量是 0 ,在第一次添加的时候进行了扩容。扩容后才达到容量为 10。

Arraylist 每次扩容都是原来的 1.5 倍 扩容的时候需要通过 Arrays.copyOf() 方法完成新数组的创建和元素赋值,代价非常高。所以当使用的时候,可以提前预知数据时,可以通过有参构造 或使用 ensureCapacity(int minCapacity) 方法 指定容量。

add( int index, E element )

 public void add(int index, E element) {
     	// 校验下标
        rangeCheckForAdd(index);
		// 这块和上面的 add 方法是一样的流程
        ensureCapacityInternal(size + 1);  // Increments modCount!!
     	// 采用 arraycopy 的方式将 index 位置挪动出来
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
     	// 插入数据
        elementData[index] = element;
        size++;
    }

删除方法 remove

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);
    	// 将最后一个下标置空, 原因:明确告诉 GC 清理
        elementData[--size] = null; 

        return oldValue;
    }

remove( Object o )

public boolean remove(Object o) {
    	// 删除 null 元素
        if (o == null) {
            // size.for
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    // 这里和上面的删除一样 都是通过 System.arraycopy 
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

获取方法 get

public E get(int index) {
    	// 校验下标
        rangeCheck(index);
    	// 返回数据
        return elementData(index);
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值