点赞在看,养成习惯。java
点赞收藏,人生辉煌。面试
点击关注【微信搜索公众号:编程背锅侠】,防止迷路。编程
List
接口的可调整大小的数组实现。数组
数组:一旦初始化长度就不能够发生改变 。微信
增删慢:每次删除元素,都须要更改数组长度、拷贝以及移动元素位置。数据结构
查询快:因为数组在内存中是一块连续空间,所以能够根据地址+索引的方式快速获取对应位置上的元素。工具
private static final int DEFAULT_CAPACITY = 10;
复制代码
private static final Object[] EMPTY_ELEMENTDATA = {};
复制代码
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
复制代码
ArrayList
的底层数据结构,transient
表示该字段不进行序列化操做transient Object[] elementData;
复制代码
ArrayList
的大小,就是集合中元素的个数private int size;
复制代码
Constructor | Constructor描述 |
---|---|
ArrayList() | 构造一个初始容量为十的空列表。 |
ArrayList(int initialCapacity) | 构造具备指定初始容量的空列表。 |
ArrayList(Collection<? extends E> c) | 构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序。 |
ArrayList()
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
复制代码
@Test
public void test_get(){
// 运行这行代码真的会构造一个初始容量为10的集合吗?
List<Integer> list = new ArrayList<>();
}
复制代码
@Test
public void test_get(){
try {
List<String> list = new ArrayList<>();
Field field = list.getClass().getDeclaredField("elementData");
field.setAccessible(true);
int size = ((Object[]) field.get(list)).length;
System.out.println(size);// 0 事实证实并无初始化一个容量为10的集合
// 添加第一个元素
list.add("1");
Field field2 = list.getClass().getDeclaredField("elementData");
field2.setAccessible(true);
int size2 = ((Object[]) field2.get(list)).length;
System.out.println(size2);// 10 事实证实添加第一个元素的时候才进行初始化容量10
} catch (Exception e) {
e.printStackTrace();
}
}
复制代码
结论:经过以上的代码演示证实空参构造方法建立集合对象并未构造一个初始容量为十的空列表,仅仅将DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的地址赋值给elementData。只有添加第一个元素的时候才会将容量初始化为10post
ArrayList(int initialCapacity)
public ArrayList(int initialCapacity) {
// 容量大于0,按照指定的容量初始化数组
if (initialCapacity > 0) {
// 建立一个数组,且指定长度为initialCapacity
this.elementData = new Object[initialCapacity];
// 若是initialCapacity容量为0,把EMPTY_ELEMENTDATA的地址赋值给elementData
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
// 容量小于0,抛非法异常
} else {
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
}
}
复制代码
@Test
public void test_c(){
// 建立一个带有初始容量的集合,这个集合建立之后真的初始容量为5吗?
List<Integer> list = new ArrayList<>(5);
}
复制代码
@Test
public void test_get(){
try {
// 构造一个长度为5的集合
List<String> list = new ArrayList<>(5);
Field field = list.getClass().getDeclaredField("elementData");
field.setAccessible(true);
int size = ((Object[]) field.get(list)).length;
System.out.println(size);// 5 确实是咱们本身指定的容量
} catch (Exception e) {
e.printStackTrace();
}
}
复制代码
结论:根据
ArrayList
构造方法参数建立指定长度的数组。测试
按照集合迭代器返回的顺序,构造一个list包含指定集合的元素。c参数:元素将被放到list中的集合指定的集合为null,将会抛出NullPointerExceptionthis
ArrayList(Collection<? extends E> c)
public ArrayList(Collection<? extends E> c) {
// 将给定的集合对象转成数组,且将数组的地址赋值给elementData
elementData = c.toArray();
// 将elementData的长度赋值给集合长度size,且判断是否不等于 0
if ((size = elementData.length) != 0) {
// 判断elementData 和 Object[] 是否为不同的类型
// c.toArray()数组不是object数组进行转换成Object[]
// 每一个集合的toarray()的实现方法不同,因此须要判断一下,若是不是Object[].class类型,那么就须要使用ArrayList中的方法去改造一下。
// elementData.getClass()究竟是什么类型的?下面搞个例子测试一下
if (elementData.getClass() != Object[].class)
// 转换Object[] 【在个人其余文章中的新增源码里面有分析】
// 若是不同,使用Arrays的copyOf方法进行元素的拷贝
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// 给定的数组的长度为0,用空数组代替
this.elementData = EMPTY_ELEMENTDATA;
}
}
复制代码
toArray()
方法及其实现源码// Collection<E>接口中转数组接口
// 返回一个包含此集合中全部元素的数组。这个方法能够保证给定集合的顺序返回数组。此方法充当基于数组的API和基于集合的API之间的桥梁。
Object[] toArray();
// ArrayList<E>中的toArray()方法
public Object[] toArray() {
// 调用数组工具类方法进行拷贝
return Arrays.copyOf(elementData, size);
}
复制代码
Arrays类中的copyOf方法
源码// Arrays类中的copyOf方法进行数组的拷贝。original原始的数组,newLength新的容量
public static <T> T[] copyOf(T[] original, int newLength) {
// 再次调用方法进行拷贝
return (T[]) copyOf(original, newLength, original.getClass());
}
// 将原始的数组copy到新的容量的数组中的具体实现
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);
// 将数组的内容拷贝到 copy 该数组中,使用System.arraycopy 将须要插入的位置(index)后面的元素通通日后移动一位
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
// 返回拷贝元素成功后的数组
return copy;
}
复制代码
arraycopy
方法/* * @param src the source array.原始的数组 * @param srcPos starting position in the source array.在原始数组中开始的位置 * @param dest the destination array.目标数组 * @param destPos starting position in the destination data.在目标数组中的起始位置 * @param length the number of array elements to be copied.要copy的元素的个数 */
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
复制代码
elementData.getClass()
究竟是什么类型@Test
public void test_c(){
List<Integer> list = new ArrayList<>();
list.add(1);
// getClass()这个方法返回的是该对象的运行时类。
Class<? extends Object[]> aClass = list.toArray().getClass();
System.out.println(aClass);// class [Ljava.lang.Object;
if (aClass != Object[].class){
System.out.println(false);
}else {
System.out.println(true);// 打印结果:true
}
}
复制代码
Arrays.copyOf方法
接下来验证一下@Test
public void test_arrays_copy_of(){
Object[] str = new Object[]{"1", "2"};
// 1表明的是要拷贝元素的个数
Object[] arr_ = Arrays.copyOf(str, 1);
System.out.println(Arrays.toString(arr_));
// [1]
Object[] str0 = new Object[]{"3", "4"};
Object[] arr0_ = Arrays.copyOf(str0, 2);
System.out.println(Arrays.toString(arr0_));
// [3, 4]
String[] str1 = new String[]{"5", "6"};
String[] arr1_ = Arrays.copyOf(str1, 5);
System.out.println(Arrays.toString(arr1_));
//[5, 6, null, null, null] 元素不够会用null填充
}
复制代码
第一篇:ArrayList中的构造方法源码在面试中被问到了...抱歉没准备好!!!告辞
第二篇:面试官让我讲ArrayList中add、addAll方法的源码...我下次再来
第三篇:工做两年还没看过ArrayList中remove、removeAll、clear方法源码的都来报道吧
创做不易, 很是欢迎你们的点赞、评论和关注(^_−)☆
你的点赞、评论以及关注是对我最大的支持和鼓励,而你的支持和鼓励
我继续创做高质量博客的动力 !!!