JDK源码分析-ArrayList

继承结构

我们都知道数组定义了长度就不可以改变了,而List其实就是可延长的数组,内部就是采用数组结构来实现的,具体怎么实现的,我们往下来看源码,首先是ArrayList 的继承结构如下:

构造方法

 private transient Object[] elementData;

 private int size;

首先我们从ArrayList的构造方法来看

我们可以看到super()调用父类AbstractList的构造方法,而AbstractList方法是没有任何内容的所以不需要管,

然后初始化10个长度的数组。

 

如果初始化传入了集合,则直接集合转为数组,如果转换的对象类型不一致,则重新复制数组到新数组,这里因为toArray()如果被重写,那么返回类型不会直接是Object[].

 

扩容实现原理 

上面分析了 ArrayList 的构造器,但 ArrayList 如何做到动态扩容呢?

我们可以从 add() 方法着手进行分析(addAll() 方法类似,不再单独分析),如下:

   if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;

可以看到,如果扩容后的长度小于原来长度,则直接返回原来的数组长度

 

       if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);

但是如果扩容后的长度大于MAX_ARRAY_SIZE(2147483639)的长度,则直接分配系统默认最大的数值2147483647

最后通过底层调用Arrays.copyOf[ System.arraycopy]方法复制数组

由此可以看出,新容量为原容量的 1.5 倍;若扩容为 1.5 倍后,仍未达到所需容量,则直接使用所需要的容量,如果还未达到容量,则使用设置的默认值容量。

集合删除

    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; // Let gc do its work

        return oldValue;
    }

小结:    集合删除元素通过把数组的元素通过System.arraycopy复制功能往前移动一位,最后进行数组最后一位清空,gc自动回收,缺点:效率低,数组重新复制。

复制方法包括:   System.arraycopy ,  Arrays.copyof也是调用System.arraycopy, clone,for循环复制,效率最高的是System.arraycopy调用了jvm的底层。

 集合查询

   public E get(int index) {
        //检查位置
        rangeCheck(index);
        //直接通过数组下标获取
        return elementData(index);
    }


    @SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];
    }

小结:之所以ArrayList的速度快,是因为直接通过数组下标获取数据。

线程安全性

线程安全可以简单理解为:多个线程同时操作一个方法或变量时,不会出现问题;若出现问题,可认为是线程不安全的。

 ArrayList 是线程不安全的,主要体现有二:

        1、多个线程往 ArrayList 添加数据时(扩容时),可能会产生数组越界异常(ArrayIndexOutOfBoundsException);  

如果初始化长度为5的数组,添加一个元素,第一个线程拿到5进行扩容,第二个线程拿到了6而不进行扩容,就会出现下标异常。

       2、线程遍历同一个 ArrayList,有线程对其进行修改时,可能会抛出 ConcurrentModificationException。

第一个线程进行遍历,而第二个线程进行删除,第一个线程遍历的对象出现了问题,然后就会抛出ConcurrentModificationException异常

 

如果需要把ArrayList变成线程安全的,

   1.可通过 Collections.synchronizedList(list)方法实现,它会自动将我们的list方法进行改变,最后返回给我们一个加锁了List。

   2.可使用Vector集合,速度慢,但是线程安全,方法都经过Sync同步过。

 

基本的API

 booleanadd(E e) 
          将指定的元素添加到此列表的尾部。
 voidadd(int index, E element) 
          将指定的元素插入此列表中的指定位置。
 booleanaddAll(Collection<? extends E> c) 
          按照指定 collection 的迭代器所返回的元素顺序,将该 collection 中的所有元素添加到此列表的尾部。
 booleanaddAll(int index, Collection<? extends E> c) 
          从指定的位置开始,将指定 collection 中的所有元素插入到此列表中。
 voidclear() 
          移除此列表中的所有元素。
 Objectclone() 
          返回此 ArrayList 实例的浅表副本。    
 booleancontains(Object o) 
          如果此列表中包含指定的元素,则返回 true。
 voidensureCapacity(int minCapacity) 
          如有必要,增加此 ArrayList 实例的容量,以确保它至少能够容纳最小容量参数所指定的元素数。
 Eget(int index) 
          返回此列表中指定位置上的元素。
 intindexOf(Object o) 
          返回此列表中首次出现的指定元素的索引,或如果此列表不包含元素,则返回 -1。
 booleanisEmpty() 
          如果此列表中没有元素,则返回 true
 intlastIndexOf(Object o) 
          返回此列表中最后一次出现的指定元素的索引,或如果此列表不包含索引,则返回 -1。
 Eremove(int index) 
          移除此列表中指定位置上的元素。
 booleanremove(Object o) 
          移除此列表中首次出现的指定元素(如果存在)。
protected  voidremoveRange(int fromIndex, int toIndex) 
          移除列表中索引在 fromIndex(包括)和 toIndex(不包括)之间的所有元素。
 Eset(int index, E element) 
          用指定的元素替代此列表中指定位置上的元素。
 intsize() 
          返回此列表中的元素数。
 Object[]toArray()        
          按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组。
<T> T[]
toArray(T[] a) 
          按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。
 voidtrimToSize() 
          将此 ArrayList 实例的容量调整为列表的当前大小。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员阿军

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值