ArrayList简介
-
概念
ArrayList是一个容量能够动态增长的动态数组。和java的数组相比,它的容量能够动态增长。它继承了AbstractList,实现了List、RandomAccess、Cloneable、java.io.Serializable
-
继承关系
首先,我们先查看下ArrayList在java集合体中所在的位置:
在这里我们可以看到ArrayList的根部是Collection,下面我们看分析下ArrayList的继承关系
上图清晰的描述到,实现四个接口继承一个抽象类:
- ArrayList继承AbstractList;实现List。它是一个数组队列,提供了新增、修改、删除和遍历等功能。
- ArrayList实现了RandomAccess,即提供了随机访问功能。
- ArrayList实现了Cloneable,即覆盖了函数clone(),能够被克隆。
- ArrayList实现了java.io.Serializable,这表明ArrayList可以被序列化,能通过序列化去传输。
- 和Vector不同,ArrayList操作的是线程不安全的,建议在单线程的情况下使用,如果是多线程的情况下建议使用Vector和CopyOnWriteArrayList
-
从ArrayList源码分析
1.ArrayList属性:
ArrayList属性主要有当前数组长度size、存放数组对象elementData数组和代表ArrayList修改次数从AbstractList继承的modCount属性。
// 序列化id
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 = {};
// 当前数据对象存放地方
transient Object[] elementData; // non-private to simplify nested class access
// 当前数组的长度
private int size;
2.ArrayList的构造方法:
ArrayList构造方法有三种,分别是默认不传参、初始化容量的空数组和包含元素的数组。
- 无参构造函数,初始容量为10的空数组,如下:
// 第一种,调用ArrayList(10),其中初始化的一个长度为10的object数组
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
- 初始化大小的构造函数,如下:
public ArrayList(int initialCapacity) {
// 如果初始化大小小于0会报异常,否则新建初始化为object的数组
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
- 带collection对象的构造方法,
1)先将集合转换成数组,然后赋值给elementData;
2)将数组长度赋值给size并判断是否为0,
如下:
public ArrayList(Collection<? extends E> c) {
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;
}
}
1.0、这边谈一下ArrayList(Collection<? extends E> c)是深拷贝还是浅拷贝,测试代码如下:
/** 测试ArrayList(Collection<? extends E> c)是浅拷贝还是深拷贝 **/
List<BeanDemo> beanDemos = new ArrayList<>();
BeanDemo beanDemo = new BeanDemo();
beanDemo.setName("test1");
beanDemos.add(beanDemo);
List<BeanDemo> beanDemoList = new ArrayList<>(beanDemos);
beanDemo.setName("test2");
System.out.println(beanDemoList.get(0).getName());
//结果输出test2
1.2、深入研究一下源码,为什么ArrayList(Collection<? extends E> c)构造函数是浅拷贝,其中调用了Arrays.copyof()方法,传参是原始数组和拷贝数组的长度,copyof()通过获取一个原始数组的副本,被截断或用null填充以返回指定的长度。源码如下,到这一步还看不出是到底是浅拷贝还是深拷贝:
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
1.3、copyof()方法中主要使用System.arraycopy()方法来实现列表拷贝,再往下查看System.arraycopy(),发现这个是一个原生native的函数(native方法不是用java实现的),我只能仔细阅读一下这个原生方法的注释,参见文章最下方,注释内容比较长,但第一段注释直接告诉了我们arraycopy()方法拷贝的是数组的引用地址,所以它属于浅拷贝:A subsequence of array components are copied from the source array referenced by src
to the destination array referenced by dest
.
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
2.0、为何要判断elementData.getClass() != Object[].class,当我们查看c.toArray()源码的时候肯定认为返回的就是Object[]数组,为什么还需要判断呢?在查阅了各种资料之后才知道Arrays含有一个asList方法,Arrays.asList
体现的是适配器模式,只是转换接口,后台的数据仍是数组。
用代码来证实下:
List<String> llll = Arrays.asList("zhen", "xiang");
System.out.println(llll.toArray().getClass());
//返回结果:class [Ljava.lang.String;
到这ArrayList集合的三种构造方法就讲完了。下一篇会继续讲解它的其他方法~~~
借鉴资料:
1.源码分析:
https://blog.csdn.net/augfun/article/details/82323164
https://baijiahao.baidu.com/sid=1637926321175819771&wfr=spider&for=pc
2.判断toArray()方法是否返回Object[]:
https://blog.csdn.net/weixin_43390562/article/details/101236699
3.判断ArrayList(Collection<? extends E> c)是深拷贝还是浅拷贝:
https://blog.csdn.net/king0406/article/details/103752855