注: 拉钩教育大数据训练营java基础笔记 + 自己补充扩展
ArrayList 简介
ArrayList是一个其容量能够动态增长的动态数组。它继承了AbstractList,实现了List、RandomAccess(标注该类可随机访问), Cloneable(可以克隆拷贝), java.io.Serializable(可序列化)。
基本的ArrayList,长于随机访问元素,但是在List中间插入和移除元素时较慢。同时,ArrayList的操作不是线程安全的!一般在单线程中才使用ArrayList,而在多线程中一般使用Vector或者CopyOnWriteArrayList。
Vector 和 CopyOnWriteArrayList 介绍
源码剖析
基础内容
成员对象
//序列号id
private static final long serialVersionUID = 8683452581122892189L;
//transient 标注该对象 不能被序列化;ArrayList元素存储该对象中
transient Object[] elementData;
// 集合中的元素数量,区别capacity
private int size;
//初始容量capacity大小
private static final int DEFAULT_CAPACITY = 10;
//集合对数组元素的操作次数
protected transient int modCount = 0;
/**
* 如果 elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* 表示 该对象还从未添加过元素,modCount为0.
* 但elementData == EMPTY_ELEMENTDATA 只说明这个集合元素为空,
* 可能之前有过添加删除元素的操作
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//预留8个大小空间给jvm存储数组对象头里数组长度_length
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
构造函数
有3种初始化ArrayList对象方法
//初始集合对象存储元素为空,容量为0.
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/*
* initialCapacity as ic
* ic > 0 则该对象容量为 ic ,即elementData 大小为ic
* ic == 0 用EMPTY_ELEMENTDATA 赋值elementData 标注空集合
* ic < 0 抛非法数据异常
*/
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);
}
}
/**
* 将集合c中的元素拷贝到elementData素组中
* bug-6260652:elementData = c.toArray() 后elementData数组
* 不一定可以存储Object对象,得加个判断处理下。
* 如果赋值后不是Objectd对象数组,得转换下
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// defend against c.toArray (incorrectly) not returning Object[]
// (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
补充:Arrays.copy的方法 和 Systems.copy
注: 有关Arrays.ArayList.toArray 和 ArrayList.toArray() 需补充
元素操作
添加
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
//这个辅助方法是从add(E)方法分离而来的,为了保持方法字节码低于35,这将有助于add(E)方法调用C1编译循环
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
//将集合c中元素添加到数组末尾,可能需要扩容
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
modCount++;
int numNew = a.length;
if (numNew == 0)
return false;
Object[] elementData;
final int s;
if (numNew > (elementData = this.elementData).length - (s = size))
elementData = grow(s + numNew);
System.arraycopy(a, 0, elementData, s, numNew);
size = s + numNew;
return true;
}
//将集合c元素添加到数组指定位置,可能需要扩容
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
modCount++;
int numNew = a.length;
if (numNew == 0)
return false;
Object[] elementData;
final int s;
if (numNew > (elementData = this.elementData).length - (s = size))
elementData = grow(s + numNew);
int numMoved = s - index;
if (numMoved > 0)
System.arraycopy(elementData, index,
elementData, index + numNew,
numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size = s + numNew;
return true;
}
扩容
当元素数量大于等于elementData容量时,就需要为elementData数组扩容。
//方法为不能外部访问扩容
private Object[] grow() {
return grow(size + 1);
}
//newCapacity(minCapacity) 取得新数组容量大小
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
}
//java.lang.Arrays
/*
* 1.创建指定长度的某种类型的数组copy
* 2.调用本地方法将源数组元素拷贝到copy,并返回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);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
上述扩容时确定新数组容量大小数值newCapacity(minCapacity)源码:
/*
* 新容量 as newCapacity
* 旧容量 as oldCapacity
* 预估的容量参数 as minCapacity
* 1.newCapacity 为 oldCapacity 的1.5倍
* 2.如果 newCapacity <= minCapacity
* 2.1 如果elementData是默认的空数组(数组为空之前且未操作过数据) return DEFAULT_CAPACITY 和 minCapacity 最大值(因为可能minCapacity < 0)
* 2.2 判断是否minCapacity < 0 抛异常
* 3. 步骤2为false
* 3.1 newCapacity < MAX_ARRAY_SIZE 返回 newCapacity
* 3.2 否则 返回 hugeCapacity(minCapacity)(最大容量)
*/
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity <= 0) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
return Math.max(DEFAULT_CAPACITY, minCapacity);
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return minCapacity;
}
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);
}
/*
* min 大于 MAX_ARRAY_SIZE 不是返回min 而是直接返回整型最大值Integer.MAX_VALUE
*/
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE)
? Integer.MAX_VALUE
: MAX_ARRAY_SIZE;
}
我发现只有当数组扩容时才会判断数组容量大小minCapacity会不会超过MAX_ARRAY_SIZE,而在初始化阶段并没有检测语句;如果我在初始化ArrayList时直接将容量写为MAX_ARRAY_SIZE 或 MAX_ARRAY_SIZE/2 都超出内存异常.
测试代码:
List<Integer> list = new ArrayList<>((Integer.MAX_VALUE- 8)/2);
System.out.println(list.size());
测试结果:
减少不必要的容量
//使容量大小与元素个数对齐,减少空间浪费
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
查看数组下标
//首次出现元素o
public int indexOf(Object o) {
return indexOfRa
nge(o, 0, size);
}
//从后往前数,首次出现元素o;原理与indexOf(Object o)一样
public int lastIndexOf(Object o) {
return lastIndexOfRange(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;
}
转换数组
转换为数组有两个方法,一个返回Object数组;一个返回指定类型数组。
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
// 如果a的长度小于size 返回一个新的运行时动态数组,反之则在在a数组上拷贝于element元素
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
eg:
List<String > list = new ArrayList<>();
list.add("123");
list.add("58");
String [] a = {"5","s","qq","12","ss","qq","ss"};
list.toArray(a);
for (String b :a) System.out.print(b+" ");
System.out.println();
a=new String[]{"1"};
list.toArray(a);
for (String b :a) System.out.print(b+" ");
结果:
修改指定下标元素
源代码里用checkIndex(int index, int length)检测小标是否越界异常
public E set(int index, E element) {
Objects.checkIndex(index, size);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
//java.lang.Obects
public static
int checkIndex(int index, int length) {
return Preconditions.checkIndex(index, length, null);
}
//jdk.internal.util.Preconditions
//BiFunction 是一个函数式接口
public static <X extends RuntimeException>
int checkIndex(int index, int length,
BiFunction<String, List<Integer>, X> oobef) {
if (index < 0 || index >= length)
throw outOfBoundsCheckIndex(oobef, index, length);
return index;
}
删除元素
//删除指定元素
public boolean remove(Object o) {
final Object[] es = elementData;
final int size = this.size;
int i = 0;
found: {
if (o == null) {
for (; i < size; i++)
if (es[i] == null)
break found;
} else {
for (; i < size; i++)
if (o.equals(es[i]))
break found;
}
return false;
}
fastRemove(es, i);
return true;
}
//快速删除指定下标元素,私有方法,对象不能直接访问调用fastRemove
private void fastRemove(Object[] es, int i) {
modCount++;
final int newSize;
if ((newSize = size - 1) > i)
System.arraycopy(es, i + 1, es, i, newSize - i);
es[size = newSize] = null;
}
//删除数组每个元素,都赋值为null
public void clear() {
modCount++;
final Object[] es = elementData;
for (int to = size, i = size = 0; i < to; i++)
es[i] = null;
}
取与集合c交集的元素,batchRemove略微复杂,粗看第一遍没看懂。
public boolean retainAll(Collection<?> c) {
return batchRemove(c, true, 0, size);
}
//complement 为 true 则取与c交集元素,否则取差集
boolean batchRemove(Collection<?> c, boolean complement,
final int from, final int end) {
Objects.requireNonNull(c);
final Object[] es = elementData;
int r;
// Optimize for initial run of survivors
for (r = from;; r++) {
if (r == end)
return false;
if (c.contains(es[r]) != complement)
break;
}
int w = r++;
try {
for (Object e; r < end; r++)
if (c.contains(e = es[r]) == complement)
es[w++] = e;
} catch (Throwable ex) {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
System.arraycopy(es, r, es, w, end - r);
w += end - r;
throw ex;
} finally {
modCount += end - w;
shiftTailOverGap(es, w, end);
}
return true;
}
// 移动数组,清除数组末尾 hi - lo 长度的元素
private void shiftTailOverGap(Object[] es, int lo, int hi) {
System.arraycopy(es, hi, es, lo, size - hi);
for (int to = size, i = (size -= hi - lo); i < to; i++)
es[i] = null;
}
内部类
ArrayList 私有内部类 Itr Itr作为顺序迭代器对象
arrayList 私有内部类 ListItr 实现了Itr,增强型的顺序遍历迭代器,支持从后向前遍历元素
ArrayList 内部类SubListSubList对象代表一个List的某个区间中的所有元素的集合
ArrayList 内部类 ArrayListSpliterator 在ArrayList中的spliterator方法中返回的正是ArrayListSpliterator对象,ArrayListSpliterator是ArrayList中实现的并发迭代器