ArrayList源码解读

一、字段、接口、父类

        List是一个线性结构的容器,其中实现类ArrayList底层是一个Object数组,它使用了泛型,向集合添加不属于泛型的元素时,编译就会报错,而且在获取元素的时候,get()方法会根据泛型对元素进行强制转型。

        //注意,不写泛型则默认为object
		List<Integer> list1 = new ArrayList<>();
        list1.add(1);
		//注意看,这里的泛型没有写全
		//这里将list1的元素全部添加到list中,编译器并不会报错,当然写全就会报错
        List<Date> list = new ArrayList(list1);
        list.add(new Date());
        //list.add(2);  -- 这里就会报错了,因为我们想把整型添加到泛型申明为Date的集合中
        list.get(0).getTime();   

        编译期不报错,运行时抛出(ClassCastException:class java.lang.Integer cannot be cast to class java.util.Date),因为这里位于索引0的元素是整型 1 ,仅仅调用get()不会抛出异常,但是我们想要使用整型调用Date类的方法。

这个地方要注意的只是需要把泛型写全,没其他意思。

实现了Serializable接口

        ArrayList实现了Serializable接口,但是关于底层的数组,即真正的存储元素的容器是被transient修饰的,所以此数组不能被序列化,但那又为什么要实现Serializable接口呢?

​ 在对ArrayList对象进行序列化的时候,它会自动调用内部定义的writeObject(ObjectOutputStream )方法(对象流调用了writeObject(object)方法,被序列化的对象也调用writeObject方法)。它的作用是什么呢,序列化就是将对象的信息转化为二进制流从而可以存储在文件中或者在网络中传输,于是我们需要这个二进制流 小但是不缺失信息,但是对于ArrayList,它的数组大小可能很大但是存储的元素却很少(可以在构造器中指定数组大小,然后只加入一个对象),序列化为二进制流时,我们不需要那些不必要的值(0、NULL…),所以ArrayList内部定义了这个方法,对于底层的数组只序列化size的大小,size大小为数组中真实元素的个数,从而节省了空间。

 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 behavioral compatibility with clone()
        s.writeInt(size);

        // Write out all elements in the proper order.
        for (int i=0; i<size; i++) {
            s.writeObject(elementData[i]);
        }

        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }

        至于serialVersionUID没什么好说的,对序列化和反序列化有一个唯一标识。

DEFAULT_CAPACITY

        如果没有在构造方法中指定数组大小,则默认容量为10

private static final int DEFAULT_CAPACITY = 10;
EMPTY_ELEMENTDATA

        这里有个奇怪的点:

ArrayList中有两个属性:

private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

两个差不多的属性:按照属性名来看的话一个是 空的ELEMENTDATA,一个是默认空的ELEMENTDATA 。这里在构造方法中还会提到。

elementData

        这个就是ArrayList中真实的存储数据的数组了

transient Object[] elementData;

        还有一个size属性,是数组中真实存储的元素的个数,简单来说就是加入元素size增加,移除元素size减少。

MAX_ARRAY_SIZE

        底层数组长度最大值:

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
从抽象父类中继承来的modcount

        每修改一次底层数组(只算增删),modcount++,目前不知道干啥的

二、构造方法

1、空参构造器

        默认将DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋值给底层数组。

2、有参构造器

        1)、构造器参数是Collection<? extends E>,即当前构造器只接受类型参数是当前类型参数的子类的实现了Collection接口的对象,如果Collection长度为0,elementData被赋值为EMPTY_ELEMENTDATA。

        2)、构造器参数是整型,指定底层数组的大小,如果指定大小为0,则把EMPTY_ELEMENTDATA赋给elementData。

三、其他方法

grow()方法

        显然ArrayList底层是一个数组,那么为什么ArrayList比直接使用数组更方便呢,一大原因即:数组初始化过后不能扩展,ArrayList容器为我们做了底层数组扩展的事情。 那么它是怎样扩展的呢。

        简单来说就是在向ArrayList容器中add元素的时候,它会判断size == elementData.length,如果是true,会进行扩容,扩容为1.5倍(oldCapacity + (oldCapacity >> 1);),如果扩容后还是不够(调用方法时它会传入minCapacity,大小为size+1),多半发生在没有指定容量(那么之后默认容量为Math.max(DEFAULT_CAPACITY, minCapacity)),或者指定容量为0、1(之后容量为minCapacity)。

clone()方法

        重写的clone()方法是浅拷贝。

listIterator()方法

        list专用迭代器方法,可以进行前后双向遍历,ArrayList中有实现了Iterator接口的内部类和另一个实现了ListIterator的内部类。而Iterator()方法和listIterator()方法就是返回这些内部类实例的方法。

subList(int fromIndex, int toIndex)方法

        截取ArrayList中的一段,返回值也是ArrayList的内部类,内部类方法和ArrayList的方法差不多。

foreach(consumer …)方法

        接受一个消费者接口,遍历所有元素对其进行操作,和Stream流的操作一样。

集合类不可少的增删改查方法

。。。

四、总结

        ArrayList底层是一个数组,它是一个查找、更新效率高,增删效率低的一个结构,因为增删会频繁地进行数组的复制操作。默认数组长度为10,当增加元素时,元素个数等于数组长度就会开始扩容,扩容倍数是1.5(当扩容后依旧不够,新容量=旧容量+1)。
        为什么ArrayList可以使用for(xx : xx) 语法,因为此语法内部调用迭代器,一般称此语法为foreach(增强for循环),注意此语法接受每个容器内的元素并赋值给一个局部变量,修改局部变量不会对容器产生影响。

五、关于Iterable接口和Iterator接口

        Iterable接口中有可以一个返回值为Iterator接口的方法,Iterable接口指可以迭代,而Iterator接口才是真正的迭代器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值