import java.util.Arrays;
import java.util.Objects;
import jdk.internal.util.ArraysSupport;
import jdk.nashorn.api.tree.ForInLoopTree;
import sun.jvm.hotspot.utilities.GrowableArray;
import sun.security.util.Length;
/**
* @Classname ArrayList:是Java集合框架中List接口的一个实现类
* @note:容量可以动态增长,必然存在扩容和复制,必然影响性能,数据有序的(元素输出和输入顺序一致),元素可为null
* @note:占用空间小,对比LinkedList,不用占用额外空间维护链表结构
* @note:ArrayList跟数组一样,适合于随机访问,不太适合于大量的插入与删除,如果一定要进行大量插入与删除:
* (1)根据数量级,使用ArrayList(int initialCapacity)有参构造
* (2)使用LinkedList,这是一个适合于增删的集合类
* @note:size(),isEmpty(),get(),set(),iterator(),ListIterator()方法的时间复杂度都是O(1)
* @note:add()添加操作的时间复杂度平均为O(n)
* @note:其他所有操作的时间复杂度几乎都是O(n)
*/
// ArrayList还实现了三个标识型接口,这三个接口都没有任何方法,仅作为标识表示实现类具备某项功能
// RandomAccess表示实现支持快速随机访问,Cloneable表示实现类支持克隆,具体表现为重写了clone方法,
// java.io.Serializable则表示支持序列化,如果需要对此过程自定义,可以重写writeObject与readObject方法
public class ArrayList<E> extends AbstractList<E> implements List<E>,RandomAccess,Cloneable,java.io.Serializable {
/**
** ArrayList的成员变量
**/
// 由于数组类型为Object,所以允许添加null,当添加进一个元素后,就会直接扩展到DEFAULT_CAPACITY,也就是10
// transient说明这个数组无法序列化
// 当ArrayList扩容时,其capacity就是这个数组应有的长度,和size的区别在于,ArrayList扩容并不是需要多少就扩展多少的
transient Object[] elementData;
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 两个默认的空数组,后面构造函数中需要
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final int DEFAULT_CAPACITY = 10; // 数组初始化容量为10
private int size; // 数组中当前元素个数
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;// 数组最大容量,一些虚拟器需要在数组前加个头标签,所以减去8,换算成二进制为2^31-1,即所有位均为1
/**
** @ArrayList的构造函数:ArrayList就是对数组的封装,并且支持的动态增长特性对于性能的影响需要额外的措施来保障
**/
public ArrayList() {// 用默认的空数组去构建无参构造函数,只有插入一条数据后才会扩展为10,而实际上默认是空的
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 根据指定容量,创建一个指定长度的数组。通用做法:new ArrayList(srclist.size());
// 这个有参构造函数就是为了避免大量数据时反复拷贝导致的性能下降,因此需要提前知道数据的数量级,但会占有较大的内存
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);
}
}
// 从一个其他的Collection中构造一样内容的ArrayList
// 这也展示了Collection这种抽象的魅力,可以在不同的结构间转换
public ArrayList(Collection<? extends E> c) {
Object[] a = c.toArray(); // 转换最主要的是toArray(),这在Collection中定义了
if((size = a.length)!= 0) {
if(c.getClass() == ArrayList.class) {
elementData = a;
}else {// 如果传入的集合并不是ArrayList的,就用Arrays.copyOf(深拷贝)去转换
elementData = Arrays.copyOf(a, size, Object[].class);
}
}else {
elementData = EMPTY_ELEMENTDATA;
}
}
/**
* 看下Arrays.copyOf()方法,返回类型为T的数组,容量为newLength和original.length的较小值,
* 元素为original数组中的元素,当较小值长度小于给定数组的长度时,那就是截断数组了
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
* 判断newType是否是Object数组,==比较的是内存地址,但事先得向上转为同一类型才能比较
* newType.getComponentType()返回数组内得元素类型,不是数组时,返回null
* newInstance()内部直接调用Array.newArray,去创建一个与newType一样,长度为newLength得数组
* System.arraycopy()静态本地方法,由虚拟机自己实现,第一个参数是源数组,第二个参数是源数组中开始复制的位置
* 第三个参数是目标数组,第四个是目标数组的开始赋值的位置,第五个参数是被复制的数组元素的数量
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;
}
*/
/**
* ArrayList的关键方法:遍历判断
* @note:跟AbstractList相比,完全没必要先转成Iterator再进行操作
*/
public boolean contains(Object o) {
return indexOf(o) > 0 || indexOf(o) == 0;
}
public int indexOf(Object o) {
return indexOfRange(o,0,size);
}
int indexOfRange(Object o,int start,int end) {
Object[] es = elementData;
if(o == null) {
for (int i = start; i < end; i++) {
if (es[i] == null) {
return i;
}
}
}else {
for (int i = start; i < end; i++) {
if (o.equals(es[i])) {
return i;
}
}
}
return -1;
}
/**
* ArrayList的关键方法:转换成数组
* @note:toArry()底层还是采用的数组的copyOf
* @note:跟AbstractList相比,完全没必要先转成Iterator再进行操作
*/
public Object[] toArray() {
// 转换出来的数组是超类数组,无法直接转换成其他类,需要取出每个元素进行转化
// 注意这里返回时把elementData截断为size大小
return Arrays.copyOf(elementData, size);
}
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {// 能够直接转换成所需要的数组,一般采用该方法进行重构
if (a.length < size) {
// 给定的数据长度不够,复制一个新的并返回
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
}
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size) {
a[size] = null;
}
return a;
}
/**
* ArrayList的关键方法:改查集合里的数据
*/
@SuppressWarnings("unchecked") // 告诉编译器忽略集合由于未进行参数化产生的警告信息
public E get(int index) {
Objects.checkIndex(index,size);
return (E) elementData[index];
}
public E set(int index, E element) {
Objects.checkIndex(index, size);
@SuppressWarnings("unchecked")
E oldValue = (E) elementData[index];
elementData[index] = element;
return oldValue;
}
/**
* ArrayList的关键方法:增删集合里的数据
*/
public boolean add(E e) {
modCount++;
add(e,elementData,size);
return true;
}
private void add(E e,Object[] elementData,int s) {
// 当前元素的个数等于当前数组的长度,则先进行扩容,添加元素的关键就在于扩容,直到反复添加元素后元素个数追上了数组长度后再进行扩容
if (s == elementData.length) {
elementData = grow();
}
// 扩容后即可添加元素,并且当前元素也需要加1
elementData[s] = e;
size = s + 1;
}
public void add(int index, E element) {
rangeCheckForAdd(index);
modCount++;
final int s;
Object[] elementData;
if ((s = size)==(elementData = this.elementData).length) {
elementData = grow();
}
System.arraycopy(elementData, index, elementData, index, s-index);
elementData[index] = element;
size = s + 1;
}
private Object[] grow() {
return grow(size + 1);
}
// size+1=minCapacity,表示当前数组元素个数加1,即至少需要这么多的容量
private Object[] grow(int minCapacity) {
// 添加元素的过程是:首先创建一个空数组elementData,第一次插入元素时直接扩充至10,然后如果
// elementData的长度不足,就扩充1.5倍,如果扩充完溢出了,就使用需要的长度作为elementData的长度
int oldCapacity = elementData.length;
if(oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// newlength()方法就是扩容方法
int newCapacity = ArraysSupport.newlength(
oldCapacity,minCapacity-oldCapacity,oldCapacity >> 1);
return elementData = Arrays.copyOf(elementData, newCapacity);
}else {
// 空数组第一次插入数据就直接扩充至10
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}
}
集合类源码之ArrayList(五)
最新推荐文章于 2022-10-01 23:40:49 发布