絮叨
前面2篇的基础,大家还是好好学习一下,下面是链接
本来想直接将List这个父类下的所有之类,但是怕太长,我把我们真实开发中最最常用的ArrayList单独拿出来讲了,后面2个做一篇(顺便提一下,如果是零基础的不建议来,有过半年工作经验的跟着我一起把这些过一遍的话,对你的帮助是非常大的)
一、ArrayList认识
概念
概念:ArrayList是一个其容量能够动态增长的动态数组。但是他又和数组不一样,下面会分析对比。它继承了AbstractList,实现了List、RandomAccess, Cloneable, java.io.Serializable。
RandomAccess接口,被List实现之后,为List提供了随机访问功能,也就是通过下标获取元素对象的功能。
实现了Cloneable, java.io.Serializable意味着可以被克隆和序列化。
最主要的还是看我化圈的部分
ArrayList的数据结构
分析一个类的时候,数据结构往往是它的灵魂所在,理解底层的数据结构其实就理解了该类的实现思路,具体的实现细节再具体分析。
ArrayList的数据结构是: 说明:底层的数据结构就是数组,数组元素类型为Object类型,即可以存放所有类型数据。我们对ArrayList类的实例的所有的操作底层都是基于数组的。
ArrayList源码分析
继承结构和层次关系
我们看一下ArrayList的继承结构:
- ArrayList extends AbstractList - AbstractList extends AbstractCollection 复制代码
所有类都继承Object 所以ArrayList的继承结构就是上图这样
思考 为什么要先继承AbstractList,而让AbstractList先实现List?而不是让ArrayList直接实现List?
这里是有一个思想,接口中全都是抽象的方法,而抽象类中可以有抽象方法,还可以有具体的实现方法,正是利用了这一点,让AbstractList是实现接口中一些通用的方法,而具体的类, 如ArrayList就继承这个AbstractList类,拿到一些通用的方法,然后自己在实现一些自己特有的方法,这样一来,让代码更简洁,就继承结构最底层的类中通用的方法都抽取出来,先一起实现了,减少重复代码。所以一般看到一个类上面还有一个抽象类,应该就是这个作用
RandomAccess这个接口
我点进去源码发现他是一个空的接口,为啥是个空接口呢
RandomAccess接口是一个标志接口(Marker)
ArrayList集合实现这个接口,就能支持快速随机访问
public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key) { if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD) return Collections.indexedBinarySearch(list, key); else return Collections.iteratorBinarySearch(list, key); } 复制代码
通过查看源代码,发现实现RandomAccess接口的List集合采用一般的for循环遍历,而未实现这接口则采用迭代器。
ArrayList用for循环遍历比iterator迭代器遍历快,LinkedList用iterator迭代器遍历比for循环遍历快, 所以说,当我们在做项目时,应该考虑到List集合的不同子类采用不同的遍历方式,能够提高性能! 然而有人发出疑问了,那怎么判断出接收的List子类是ArrayList还是LinkedList呢? 这时就需要用instanceof来判断List集合子类是否实现RandomAccess接口!
总结:RandomAccess接口这个空架子的存在,是为了能够更好地判断集合是否ArrayList或者LinkedList,从而能够更好选择更优的遍历方式,提高性能
ArrayList 类中的属性
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { // 版本号 private static final long serialVersionUID = 8683452581122892189L; // 缺省容量 private static final int DEFAULT_CAPACITY = 10; // 空对象数组 private static final Object[] EMPTY_ELEMENTDATA = {}; // 缺省空对象数组 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 元素数组 ArrayList中有扩容这么一个概念,正因为它扩容,所以它能够实现“动态”增长 transient Object[] elementData; // 实际元素大小,默认为0 private int size; // 最大数组容量 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; } 复制代码
构造方法
无参构造
public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } 复制代码
此时ArrayList的size为空,但是elementData的length为1。第一次添加时,容量变成初始容量大小10(默认无参构造的容量就是10)
int参数构造
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); } } 复制代码
这个比较简单 进来判断是否大于0,如果大于0 就创建一个传进来大小的一个数组, 如果为0就是空数组
collection参数构造函数
public ArrayList(Collection<? extends E> c) { // 指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排 // 这里主要做了两步:1.把集合的元素copy到elementData中。2.更新size值。 elementData = c.toArray(); if ((size = elementData.length) != 0) { // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } } 复制代码
把集合的元素重新放到新的集合里面,然后更新实际容量的大小