JDK集合框架之ArrayList源码解析

Java中为什么要有ArrayList这个集合类?

ArrayList底层的数据结构是采用数组来实现的,我们都知道数组这种结构的优点:访问数组中的元素所耗费的时间都是常数时间。但是还有一个大的缺点就是数组是定长的,也就是我们一旦创建了数组,就必须指定它的存储空间大小,而它的存储空间大小一旦指定就不会再发生改变,显然,这个缺点制约了数组在很多场合下的应用,因此,在保证常数时间访问的同时,又要实现可变长的数组,就出现了ArrayList这个集合类。

ArrayList是怎么实现数组优点的同时,又解决了数组的缺点?

聪明的你一定能想到,利用数组作为存储集合数据的成员变量,所有的集合操作都是围绕存储集合数据的数组来进行的,显而易见,实现了利用数组的优点,但如果只限于此,还是大小不可变的数组。故而,通过数组复制并重新赋值数组对象的方式来实现可变长的数组。简而言之,ArrayList是采用数组复制并重新赋值到数组对象的方式来实现的。

引申一点,利用数组作为ArrayList的成员变量,为什么要加transient修饰?

要说明这个问题首先就得说一下为什么ArrayList需要序列化?ArrayList被广泛应用于数据传输或者数据存储等,这些操作都需要将ArrayList的实例对象转换成对象输出输入流来完成。而实现Serializable接口就表示该集合对象可以转成输入输出流进行操作。

ArrayList只有两个成员变量elementData和size,实现Serializable接口在序列化时只会将该类的非静态和非transient修饰的fields进行序列化。ArrayList的设计者将elementData用transient修饰标识不将它进行自动序列化,因为传输或者存储的关注点在于数据,但是在传输或者存储的过程中,还是要以数据原来存在的结构状态(对于ArrayList就是有序的),所i以为了保证序列化前和反序列化后对象状态的一致,设计者对elementData进行手动序列化(通过writeObject和readObject方式)。看源代码也知道,只是将集合中的元素和集合大小进行了序列化。还要序列化集合大小是因为要考虑到反序列化时利用集合大小创建数组以存放序列化的元素。

总结,我们在考虑是否用transient修饰时考虑两个场景1)业务需要2)性能因素,比如前端传输了5个变量,后端用一个实体类全部接收,但返回的时候只返回这5个变量中的4个,就想到用transient,同时要对另一个进行手动序列化readObject和writeObject,这样避免新建另一个实体类。

引申一点:jdk1.8相比1.7,在ArrayList多了几个静态成员变量为什么?

jdk1.7在设计ArrayList有一个不合理之处,试想这样的一种情况:1)1.7创建ArrayList时不管用不用默认就是开辟10个空间,不合理 2)有一个公共方法trimToSize(),1.7中只有一个判断,试想当如果最后ArrayList的size大小为0时候,ArrayList的结构其实就是创建时的默认结构(10个空间)由于没有考虑为0的情况,当进行数组复制的时候,不会创建出10个空间的数组。因此,1.8对于这些情况都做了完善的考虑:新增了几个数组成员变量EMPTY_ELEMENTDATADEFAULTCAPACITY_EMPTY_ELEMENTDATA。那么它们为什么是静态类型的?为什么不是transient?第一问:我们知道静态的和transient修饰的都不会参与到序列化。第二问:可能基于这两个成员变量是类属性而不是实例属性。从1.7就可以看出ArrayList其实只需要两个实例属性elementData和size。最重要的一点应该是它们的初始化时机都要在实例属性之前。

ArrayList的含Collection参数的构造方法中为什么要判断toArray()方法返回的是不是Object类型的?

Collection接口中声明的toArray()方法返回的是Object数组,但如果有一个类继承了ArrayList并重写了toArray()方法返回为String类型的数组,当把这个继承了ArrayList的类作为参数传入到ArrayList(Collection c)的构造方法中,调用c.toArray()方法返回的是String类型的数组,而不是Object数组。这里涉及到方法调用:包含解析调用和分派调用。而JVM规定:只要能被invokestatic和invokespecial指令调用的方法,都可以在解析阶段中确定唯一的调用版本。符合这个条件的有:静态方法,私有方法,实力构造器和父类方法这4类以及被final修饰的方法,它们在类加载的时候就会把符号引用解析为该方法的直接引用。

引申一点:除了解析调用,jvm还有分派调用。


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯
ArrayList是Java集合框架中的一个类,它实现了List接口,可以用来存储一组对象,这些对象可以是任意类型。 下面是ArrayList解析: 1. 成员变量 ```java /** * Default initial capacity. */ private static final int DEFAULT_CAPACITY = 10; /** * Shared empty array instance used for empty instances. */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * Shared empty array instance used for default sized empty instances. We * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when * first element is added. */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. Any * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA * will be expanded to DEFAULT_CAPACITY when the first element is added. */ transient Object[] elementData; // non-private to simplify nested class access /** * The size of the ArrayList (the number of elements it contains). * * @serial */ private int size; ``` ArrayList有三个成员变量,分别是DEFAULT_CAPACITY、EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA。DEFAULT_CAPACITY表示默认的容量大小,EMPTY_ELEMENTDATA是一个空数组,DEFAULTCAPACITY_EMPTY_ELEMENTDATA也是一个空数组,但它会在第一次添加元素时扩容为DEFAULT_CAPACITY大小。elementData是一个Object类型的数组,用于存储ArrayList中的元素,size表示ArrayList中元素的数量。 2. 构造方法 ```java /** * Constructs an empty list with an initial capacity of ten. */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } /** * Constructs a list containing the elements of the specified * collection, in the order they are returned by the collection's * iterator. * * @param c the collection whose elements are to be placed into this list * @throws NullPointerException if the specified collection is null */ public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // defend against c.toArray (incorrectly) not returning Object[] // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } } /** * Constructs an empty list with the specified initial capacity. * * @param initialCapacity the initial capacity of the list * @throws IllegalArgumentException if the specified initial capacity * is negative */ public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } ``` ArrayList提供了三个构造方法。第一个构造方法是无参的构造方法,它将elementData赋值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA。第二个构造方法接收一个Collection类型的参数c,它将参数c中的元素转为数组并将其赋值给elementData。第三个构造方法接收一个int类型的参数initialCapacity,它根据参数initialCapacity的值创建一个Object类型的数组并将其赋值给elementData。 3. 常用方法 常用方法包括add()、get()、set()、remove()、size()等。 add()方法用于在ArrayList中添加一个元素,如果elementData的容量不足,就需要进行扩容。扩容的方式是将elementData数组的大小增加50%。 get()方法用于获取ArrayList中指定位置的元素。 set()方法用于将ArrayList中指定位置的元素替换为指定的元素。 remove()方法用于删除ArrayList中指定位置的元素。 size()方法用于获取ArrayList中元素的数量。 4. 总结 ArrayList是Java集合框架中的一个类,它实现了List接口,可以用来存储一组对象。ArrayList解析包括成员变量、构造方法和常用方法。掌握ArrayList码可以帮助我们更好地理解它的实现原理,从而更加灵活地应用它。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值