ArrayList 详解

ArrayList:  是 java 集合框架中比较常用的数据结构了。继承自 AbstractList,实现了 List 接口。底层基于数组实现容量大小动态变化。允许 null 的存在。同时还实现了 三个标记接口 RandomAccess、Cloneable、Serializable ,所以ArrayList 是支持快速访问、复制、序列化的。不是线程安全的。

AbstractList : 继承AbstractList 说明是一个集合,可以增删改查。

三个标记接口的含义:

RandomAccess: 支持随机访问(基于下标),为了能够更好地判断集合是ArrayList还是LinkedList,从而能够更好选择更优的遍历方式,提高性能!

Cloneable:支持拷贝。实现Cloneable接口,重新cloine方法,方法内容默认调用父类的方法。

Serializable : 序列化。将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来可以很轻松的传输和存储数据。Serializable是告诉JVM,我不对中给类做序列化,你帮我做好。如果我们没有自己声明一个serialVersionUID变量,接口会默认生成一个serialVersionUID,默认的serialVersinUID对于class的细节非常敏感,反序列化时可能会导致InvalidClassException这个异常(每次序列化都会重新计算该值)。

ArrayList类中的属性:

private static final long serialVersionUID = 8683452581122892189L;序列化版本号(类文件签名)。如果不写会默认生成,类内容改变会影响签名,会导致序列化失败。
private static final int DEFAULT_CAPACITY = 10;如果实例化时未指定容量,则在初次添加元素时会进行扩容使用此容量作为数组长度

static修饰,所有未指定容量的实例(也未添加元素)共享此数组,两个空的数组有啥区别?就是第一次添加元素时知道该 elementData 从空的构造函数还是有参构造函数被初始化的。以便确认如何扩容。空的构造器则初始化为10,有参构造器则按照扩容因子扩容。

private static final Object[] EMPTY_ELEMENTDATA = {};

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

transient Object[] elementData; // arrayList真正存放元素的地方,长度大于等于size

private int size;//arrayList中的元素个数

ArrayLisst构造器:

无参构造器,构造一个容量大小为10的空的list集合,但构造器只是给elementData赋值一个空的数组,只有在第一次添加元素的时候,扩容至10。

当使用无参构造器的时候是把DEFAULTCAPACITY_EMPTY_ELEMENTDATA 复制给elementData.当 initialCapacity为0的时候是把EMPTY_ELEMENTDATA赋值给elementData。当initialCapacity大于0的时候,是初始化一个大小为 initalCapacity 的object数组并赋值给 elementData。

将 Collection 转化为数组,数组长度赋值给 size。 如果 size 不为零,则判断 elementData 的 class 类型是否为 ArrayList,不是的话则做一次转换。 如果 size 为零,则把 EMPTY_ELEMENTDATA 赋值给 elementData,相当于new ArrayList(0)。

ArrayList 集合操作:

添加元素--默认尾部添加:

每次添加元素到集合中都会确认容量大小。然后给size +1。判断如果 elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA 就取 DEFAULT_CAPACITY 和 minCapacity 的最大值也就是 10。这就是 EMPTY_ELEMENTDATA 与 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的区别所在。同时也验证了上面的说法:使用无参构造函数时是在第一次添加元素时初始化容量为 10 的。

对 modCount 自增1,记录操作次数,如果modCoun的值大于elementData 的长度就进行扩容。第一次添加元素的时候 elementData的长度为0。

涉及到扩容会消耗性能,但如果提前指定容量,会提升性能,可以达到和 linkedList相当。在特定情况下甚至超越。

指定下标添加元素:

rangeCheckForAdd(index);//下标越界检查

ensureCapacityInternal(size + 1); //判断扩容,记录操作数

System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
复制插入位置及后面的元素,到后面一格,不是移动,复制完成后。添加的下标位置和下一个位置指向对同一个对象。
    elementData[index] = element;//再将元素赋值给该下标

如何扩容:

 

扩容步骤:

1. 获取elementData 当前长度

2. 默认扩容之原来容量的1.5倍。

3.如果1.5倍容量太小,将我们需要的容量 minCapacity赋值给 newCapacity

4.如果1.5倍太大或者我们需要的容量太大,那就直接拿 newCapacity = (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE 来扩容

5.将原理数组中的数据复制到大小为newCapacity的新数组中,并将新数组赋值给elementData。

 

 

remove操作

操作步骤:

public E remove(int index) {
    rangeCheck(index); // 判断下标是否越界

    modCount++; // 操作次数+1
    E oldValue = elementData(index); 

    int numMoved = size - index - 1;
    if (numMoved > 0) // 判断是否是最后一位,如果不是最后一位,就从index+1的开始往后的所有元素都向前拷贝一位,然后将最后一个元素置空,方便垃圾回收器回收
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; //如果是最后一位直接置空。

    return oldValue;
}

当我们调用 remove(Object o) 时,会把 o 分为是否为空来分别处理。然后对数组做遍历,找到第一个与 o 对应的下标 index,然后调用 fastRemove 方法,删除下标为 index 的元素。

fastRemove(int index) 方法和 remove(int index) 方法基本全部相同。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值