文章目录
- 前言
- 一.ArrayList介绍
- 二.ArrayList的部分源码学习
- 1.初始化
- 2.add方法
- (1).add(E e)
- (2).Arrays.copyOf
前言 ArrayList是我们常用到的一个重要的java数据结构。ArrayList本质上是一个数组,可以包含万物的数组,同时是一个增长的数组。下面就简单的分析下它的一些机制。本文分析基于JDK1.8
一.ArrayList介绍 【Java学习|ArrayList源码浅析】ArrayList的继承关系:
public class ArrayList extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable
ArrayList主要是继承AbstractList类和List接口
这里有一个奇怪的接口RandomAccess,其实这个是有很大的用处的,后面将详细的介绍下该接口的使用。
ArrayList与Collection关系如下图:
文章图片
ArrayList的构造函数:
// 默认构造函数
ArrayList()
// capacity是ArrayList的默认容量大小。当由于增加数据导致容量不足时,容量会添加上一次容量大小的一半。
ArrayList(int capacity)
// 创建一个包含collection的ArrayList
ArrayList(Collection extends E> collection)
二.ArrayList的部分源码学习 1.初始化 从本质上ArrayList是对Object[]数组的封装,ArrayList的设计是为了解决普通的数组只能固定长度。普通数组在使用中一方面可能会出现内存的浪费,另一方面可能会造成数组容量不够。
/**
*有参构造函数
*1.根据你输入的initialCapacity生成initialCapacity长度的Object数组
*2.如果你输入的initialCapacity=0,会使用默认的长度为0的数组
*3.如果小于0直接非法参数异常
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = https://www.it610.com/article/new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
/**
* 无参构造函数
* 当使用无参构造函数的时候,内部的创建一个默认的长度为0的数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA
*/
public ArrayList() {
this.elementData = https://www.it610.com/article/DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 有参构造函数
* 将Collection的数据作为ArrayList的一部分
* 通过toArray转换为Object[]的数组,判断长度是否为0。
*/
public ArrayList(Collection extends E> c) {
elementData = https://www.it610.com/article/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;
}
}
这个elementData数组是整个ArrayList的真正的容器。所有的操作最后都要落实到它上面。
2.add方法 ArrayList之所以被称为动态数组,在于它可以进行扩容,扩容的秘密藏于add方法中
ArrayList的add方法有两个方法分别是add(int index, E element)和add(E e)
先来看add(E e)方法。
(1).add(E e)
add方法中调用了ensureCapacityInternal方法,详细的说明已经在注释中写的很清楚了。
//add方法
public boolean add(E e) {
ensureCapacityInternal(size + 1);
// 添加一个元素,在原有的size基础上增加1,但是注意这里成员变量size的值没有被改变
elementData[size++] = e;
//添加一个元素
return true;
}//ensureCapacityInternal方法
private void ensureCapacityInternal(int minCapacity) {
//判断是否是ArrayList内部提供的默认数组
if (elementData =https://www.it610.com/article/= DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//如果是,算出minCapacity,取出DEFAULT_CAPACITY和传入的参数minCapacity的最大值
// 同时这种写法也是避免出现minCapacity出现非正数的情况
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//调用ensureExplicitCapacity方法,这时候minCapacity的值一定是大于0的
ensureExplicitCapacity(minCapacity);
}private void ensureExplicitCapacity(int minCapacity) {
//modCount是在对list,Iterator迭代的时候判断是否改变了整体的数据结构的一种标记
modCount++;
//minCapacity> elementData.length才会调用grow方法
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//在原来的数组长度的基础上增加原长度的一半。(意味着每一次添加元素,其数组的长度将增加原来的一半)
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
//使用Arrays.copyOf方法来增加原数组的的长度
elementData = https://www.it610.com/article/Arrays.copyOf(elementData, newCapacity);
}
注意这句代码:
oldCapacity >> 1
它表示oldCapacity除以2的一次方,实际上就是除以2。这种写法相对于直接写成"oldCapacity /2"的效率更高,java的源码中大量使用了这种写法。到目前为止还没有增加Object[]数组的容量,那么如何增加已经固定长度的Object[]数组大小呢?让我们看下"Arrays.copyOf"代码
(2).Arrays.copyOf
public static T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
} public static T[] copyOf(U[] original, int newLength, Class extends T[]> newType) {
// 保证copy数组是Object[]类型的
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;
}
System.arraycopy的解释
Object src : 原数组
int srcPos : 原数组的起始位置
Object dest :目标数组
int destPos :目标数组的起始位置
int length:要copy的数组的长度public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)public static native void arraycopy(Object src,intsrcPos,
Object dest, int destPos,
int length);
最后扩容是重新创建了一个比原来数组大二分之一的新数组,再通过System.arraycopy拷贝原数组数据到新数组中。相当于elementData数组扩大了原来数组的二分之一。
待续
推荐阅读
- ace File-Input实现图片上传功能+colorBox显示图片
- java学习|【算法学习】链表数相加(Java)
- Java学习|HashMap浅析
- Java学习|Android中的位运算
- Java中二维数组的拷贝方式
- java数组截取
- java数组拼接
- 图像处理|基于投影的字符版面分析java代码
- java学习|excel导出(使用window.open的post请求方式)