【Java容器】(1)ArrayList

特点

  1. 随机访问:按索引访问只需要O(1)的时间,访问第i个元素的地址=起始地址+单位长度*i
  2. 读快写慢:插入元素需要O(n)的时间
  3. 允许插入null元素
  4. 使用索引迭代比使用Iterator迭代
  5. 非线程安全

成员变量和常量

  1. private transient Object[] elementData
    使用transient关键字标记,表明这个对象不会被自动序列化,因为elementData是一个缓冲区,真正的数据部分只占其中的一部分,不需要对其全部进行序列化,java在writeObject方法中,手动控制了只序列化elementData中有数据的部分。
  2. private int size
    真正的数据部分的大小
  3. protected transient int modCount
    modCount变量用来记录 ArrayList 结构发生变化的次数。结构发生变化是指添加或者删除至少一个元素的所有操作,或者是调整内部数组的大小,仅仅只是设置元素的值不算结构发生变化。
    在进行读写(序列化或遍历)操作时,需要比较操作前后modCount是否改变,如果改变了,就会抛出ConcurrentModificationException异常。因为在进行读写操作时,如果改变了结构,那么这次操作的结果就是不正确的,需要抛出异常,即快速失败(Fail-Fast)机制。
  4. private static final int MAX_ARRAY_SIZE=Integer.MAX_VALUE-8
    默认数组长度的上限,实际上数组长度的上限可达到Integer.MAX_VALUE

构造方法

  1. public ArrayList()
    不传参数,elementData默认设置为10
  2. public ArrayList(int initialCapacity)
    指定默认容量,即elementData的长度
  3. public ArrayList(Collection<? extends E> e)
    从已有的集合创建,先设置elementData的长度为集合的大小,然后将集合的元素复制到elementData

方法

  1. 读get(index),写set(index,value),时间O(1)
  2. 添加add/addAll
    如果向末尾添加需要O(1)时间,如果向中间插入需要O(n)时间将后面的元素后移
  3. 删除remove/removeAll/retainAll
    remove中间元素需要O(n)的时间将后面的元素前移。
    removeAll(Collection c)在list中删除c中存在的元素
    retainAll(Collection c)在list中保留c中存在的元素,其他删除
    这两个方法调用了batchRemove(Collection,Boolean)方法,该方法的时间复杂度为O(m*n),因此应该谨慎使用这两个方法,可以使用hash表等空间获取时间的优化。
  4. 查找indexOf/lastIndexOf/contains
    使用顺序查找指定对象的索引,需要O(n)时间,contains直接调用了indexOf

扩容(重点)

  1. 在调用add/addAll方法时,需要调用ensureCapacityInternal(minCapacity)方法确保elementData的长度足够容纳新元素,minCapacity表示本次添加操作完成后需要的elementData的最小容量。
  2. ensureCapacityInternal(minCapacity)方法中,如果elementData为空,那么minCapacity被设置为它和10的最大值,随后调用ensureExplicitCapacity方法,如果minCapacity>elementData的长度(即现有的长度已经不能容纳新元素的添加了),就进入grow(minCapacity)方法进行扩容。
  3. grow(minCapacity)方法中:
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
    newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
    newCapacity = hugeCapacity(minCapacity);

先定义新容量为原容量的1.5倍,如果新容量不够,那么直接扩充到需要的容量(minCapacity),如果需要的容量比MAX_ARRAY_SIZE还要大,那么直接扩容到Integer.MAX_VALUE。

  1. 最后调用Arrays.copyOf方法,创建扩容后容量的数组,把旧数据拷贝进来。

注意事项

  1. 如果已知ArrayList需要的大致长度,可以在创建对象时指定容量。
  2. 如果需要大量add/addAll操作,那么可以先使用ensureCapacity方法进行手动扩容,避免多次扩容带来计算开销。
  3. 谨慎使用removeAllretainAll方法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值