【JAVA 学习笔记】ArrayList 探究

前言

文章仅是笔者个人的学习笔记,存在一些只有笔者个人能看到的用词或者描述,如果有不明确的地方,欢迎留言理性讨论。

一、概述

  1. Arraylist不是线程安全的,但是java有提供同步创建的方法:Collections.synchronizedList(new ArrayList(...));,关键点看起来是 RandomAccess 这个接口,
  2. 默认初始容量是10,在添加大容易之前,可以使用 ensureCapacity 来提前确定需要的容量及扩容,避免递增式的扩容和拷贝,(从1直接到N,避免1、2、3、、、N)。
  3. 可以存 null

二、源码分析

  1. 底层使用数组:private transient Object[] elementData;,注意这里的 transient ,如果用它声明一个实例变量,当对象存储时,它的值不需要维持(也就是不需要做序列化)。
    1. 引申1:为什么要声明这个关键字呢?
      1. 这里要 联系 Arraylist 的扩容机制:数组的容量是按一定系数扩容的,比如当前容量是20,但是可能实际只有 15 个item,如果直接把整个数组来序列化,那么就浪费了 20-15=5 个元素的空间占用。
      2. 因此,它实际是用一个 for 循环,按实际item个数,来完成序列化的。
    2. 引申2:申明了之后又是怎么实现序列化的呢?
      1. 这个可以看arraylist做序列化时候的源码来解答:
      2. java通过 Serializable 实现的序列化,本质上是通过输入输出流来完成的。
      3. 虽然底层的数组声明了 transient,但是通过下面这个方法可以看出来,每个Item被单独取出来,作为形参传入,从而实现序列化,所以数组本身声明了 transient也没有关系了。
private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
        // Write out element count, and any hidden stuff
        int expectedModCount = modCount;
        s.defaultWriteObject();

        // Write out size as capacity for behavioural compatibility with clone()
        s.writeInt(size);

        // Write out all elements in the proper order.
        //这里是重点,注意是 i < size 
        for (int i=0; i<size; i++) {
            s.writeObject(elementData[i]);
        }

        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }
  1. **构造方法:**没什么特别好说的,可以传入初始容量,初始collection集合,或者直接用默认的。
  2. **增加:**共有5个方法实现增加item。
    1. add(E e)
    2. add(int index, E element)
      1. 插入特定位置,需要对后续元素做整体后移处理。
      2. 显然这个操作是非常耗费资源的,所以arraylist不适合做插入操作。
    3. addAll(Collection<? extends E> c)
    4. addAll(int index, Collection<? extends E> c)
    5. set(int index, E element)
  3. 删除:
    1. remove(int index)
      1. 和上面的插入同理,删除特定位置的元素,后续元素整体要做前移处理
    2. remove(Object o)
    3. removeRange(int fromIndex, int toIndex)
    4. removeAll()
      1. 这个是父类实现的方法,Arraylist本身未重新实现
  4. 查找:
    1. E get(int index)
      1. 直接通过下标访问数组即可,这个速度无疑是非常快的。
      2. 所以 Arraylist 在随机访问上的效率是很高的,因为就是数组。
  5. 扩容:
    1. 实际使用的方法就是 ensureCapacity(int minCapacity) ,这里还是放下源码吧:
      1. 可以看出,其实就是换一个更大的数组,去存 Arraylist 中的数据,这过程中无疑也涉及数组的拷贝
      2. 所以这个过程的开销,可以说 懂得都懂。
    2. 每次扩容是 1.5 倍,为啥是1.5倍呢,网上说法是这个系数大小刚刚好,不会太大,也不会太小。我只能说:(待确认
    3. Arraylist 也提供了数组对齐的方法,就是把数组大小和实际元素数量对齐:trimToSize ,当然,其实就是拷贝一下。
public void ensureCapacity(int minCapacity) {
        //修改计时器
        modCount++;
        //ArrayList容量大小
        int oldCapacity = elementData.length;
        /*
         * 若当前需要的长度大于当前数组的长度时,进行扩容操作
         */
        if (minCapacity > oldCapacity) {
            Object oldData[] = elementData;
            //计算新的容量大小,为当前容量的1.5倍
            int newCapacity = (oldCapacity * 3) / 2 + 1;
            if (newCapacity < minCapacity)
                newCapacity = minCapacity;
            //数组拷贝,生成新的数组
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值