ArrayList 是实现了 List 接口的大小可变数组,实现了所有可选列表操作,运行Null在内的所有元素。
ArrayList 详解
- 从类定义可知,ArrayList 是一个列表,并且支持快速随机访问(RandomAccess)、可复制(Cloneable)、可序列化(java.io.Serializable)
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
1. ArrayList 的构造函数
- ArrayList 有 3 个构造函数
- ArrayList():初始化容量为 0 的列表。
- ArrayList(int initialCapacity):初始化指定容量的列表。
- ArrayList(Collection<? extends E> c):初始化时,将一个列表传入,将列表的每个元素对象赋值给初始化的列表。
public ArrayList() {
/* 默认的空数组 Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA= {} */
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(int initialCapacity) {
/* 创建对应长度的数组 */
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
/* 为了扩容,与空构造区别,但同样是空数组 Object[] EMPTY_ELEMENTDATA = {}*/
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
public ArrayList(Collection<? extends E> c) {
/* 将传入的 c 转化数组 */
elementData = c.toArray();
/* 传入的 c 含有元素 */
if ((size = elementData.length) != 0) {
/* 有必要的话,将传入的 c 中的元素对象转为 Object */
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// size 为0,代表 c 为空列表
this.elementData = EMPTY_ELEMENTDATA;
}
}
2. ArrayList 的静态变量
/* 版本号 */
private static final long serialVersionUID = 8683452581122892189L;
/* 默认容量大小为 10 */
private static final int DEFAULT_CAPACITY = 10;
/* 构造函数 initialCapacity 为0 是赋值给 elementData */
private static final Object[] EMPTY_ELEMENTDATA = {};
/* 构造函数 参数为空 赋值给 elementData */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/* 数据缓冲区,ArrayList 的容量就是缓冲区的长度 */
transient Object[] elementData;
/* ArrayList最大容量,为什么是 Integer.MAX_VALUE - 8,官方的解释是避免 OutOfMemoryError,但在扩容时,实际上最大容量还是 Integer.MAX_VALUE。
网上搜索一大堆 -8 是因为对象头数组有一个占 8 字节的数组数据,这没错,但是有关系吗?不想想 int 才 4 字节吗?怎么减 8 字节的长度?
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
3. 小总结
- 构造函数与静态变量构成一个列表应有的基本数据,初始化长度,列表的最大长度。
- 其他的添加、删除操作,应该属于每个实例的功能了,即非静态方法(普通方法)。
- 可能有人会问,扩容参数呢?好问题,都知道扩容为1.5倍原有的长度,即刚好可以利用右移操作,int newCapacity = oldCapacity + (oldCapacity >> 1) 源码就是这么扩容的。对于这一块知识不太懂的,可以观看我的另一篇博客,java 位运算(位与&、或|、异或^、非~、移位>>、<<)的简单理解
4. 各种操作函数
- 集合的操作函数基本都使用了泛型,有兴趣的可以去看看我的java 泛型的简单理解
4.1 添加 add()
4.1.1 boolean add(E e)
public boolean add(E e) {
/* 将 size+ 1 传入,检查添加操作,需不需要扩容 */
ensureCapacityInternal(size + 1); // Increments modCount!!
/* 给 elementData 当前长度的下一位赋值 */
elementData[size++] = e;
return true;
}
/* ensureCapacityInternal分为两步
* 1. calculateCapacity(elementData, minCapacity)
* 2. ensureExplicitCapacity(int minCapacity)
*/
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
/* 1.calculateCapacity: 判断当前的 elementData 是否空构造,
* 为 true 返回 DEFAULT_CAPACITY 与 minCapacity 的最大值
* 为 false 直接返回 minCapacity
*/
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
/* 2. ensureExplicitCapacity:
* 2.1 集合修改次数++
* 2.2 判断是否需要扩容
*/
private void ensureExplicitCapacity(int minCapacity) {
/* ArrayList 集合被修改次数,主要供迭代器 iterator 使用 */
modCount++;
// 如果列表需要的最小容量 > 当前elementData 的长度,那么进行扩展
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
- 接下来看扩容函数
/* 主要有以下 3 步
* 1. 获取列表旧容量,根据旧容量计算扩容后的新容量(1.5倍)
* 2. 比较新旧容量的值
* 2.1 newCapacity - minCapacity < 0 即扩容后的容量大小比要求的还小,直接使用需要的最小容量,这里出现的原因是添加的集合元素很多
* 2.2 newCapacity - MAX_ARRAY_SIZE > 0 即扩容后的容量比静态变量 MAX_ARRAY_SIZE 规定的最大容量还大,执行 hugeCapacity(minCapacity);
* 3. 将旧elementData 中的值 copy 到新elementData 中
*/
private void grow(int minCapacity) {
/* 1. */
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
/* 2. */
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
/* 3. 真正的扩容操作 */
elementData = Arrays.copyOf(elementData, newCapacity);
}
/* 巨大容量函数,ArrayList 的最大容量还是 Integer.MAX_VALUE,所以 MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 可能是想预留着用来干啥,没办法的话,还是的拿出来用吧 */
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
- 至此,add(E e) 操作完毕,步骤就是,判断需不需要扩容,会不会超过最大容量,不需要的话,直接进行 elementData[size++] = e; 赋值。
4.1.2 void add(int index, E element)
- 与 add(E e) 对比,多了一个 index,指定位置赋值,但必须在已有的size范围内,因为随意指定会造成极大的空间浪费
public void add(int index, E element) {
/* 对 index 位置进行判断,(index > size || index < 0),只能在size范围内进行添加,并且不能小于 0 */
rangeCheckForAdd(index);
/* 跟add()一样,判断需不需要扩容 */
ensureCapacityInternal(size + 1);
/* 因为中间被插入了一个值,所以,需要将index后面的值整体移后一位 */
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
/* 位置赋值操作 */
elementData[index] = element;
/* 有添加,元素个数 + 1 */
size++;
}
4.1.3 boolean addAll(Collection<? extends E> c)
- 将一个集合 c (不局限于ArrayList)直接添加进来
public boolean addAll(Collection<? extends E> c) {
/* 1. 将集合转换成数组,并得到数组的长度 */
Object[] a = c.toArray();
int numNew = a.length;
/* 2. 与 add() 相比,这里是直接添加多个,可能会出现扩容1.5倍比minCapacity小的情况,其余的情况相同 */
ensureCapacityInternal(size + numNew);
/* 将集合转换的数组 a 添加进 elementData */
System.arraycopy(a, 0, elementData, size, numNew);
/* 这次 add的长度是 集合转成的数组长度 */
size += numNew;
/* numNew 为0 表示没有任何元素添加进 ArrayList,返回 false */
return numNew != 0;
}
4.1.4 boolean addAll(int index, Collection<? extends E> c)
- 在指定位置添加集合
public boolean addAll(int index, Collection<? extends E> c) {
/* 检查 index 范围是都在 size 范围内 */
rangeCheckForAdd(index);
/* 获取集合长度,判断是否需要扩容 */
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
/* 计算需要移动的元素个数,先移动,后添加 */
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
/* size += 添加元素的个数 */
size += numNew;
/* 个数为0 返回 false */
return numNew != 0;
}
4.1.5 小总结
- add(E e) 是在 list 的最末尾添加一个元素,add(int index, E e) 是在指定位置(size范围内)添加一个元素
- add(E e) 结果等同于 add(arrayList.size(), E e),只不过不用整体进行 index 后的整体移位操作,虽然也没有
- addAll(Collection<? extends E> c) 将整个集合添加,若 c.size == 0 则返回false,addAll(int index, Collection<? extends E> c) 在指定位置(size范围内)添加集合
- addAll(Collection<? extends E> c) 结果等同于 addAll(int index, Collection<? extends E> c)
import java.util.ArrayList;
import java.util.LinkedList;
public class AddTest {
public static void main(String[] args) {
ArrayList<Integer> arrayList = new ArrayList<>();
System.out.println("new ArrayList()后, 当前 arraylist --> " + arrayList + "\t 当前 size " + arrayList.size());
arrayList.add(10);
System.out.println("add(1)后, 当前 arraylist --> " + arrayList + "\t 当前 size " + arrayList.size());
arrayList.add(20);
System.out.println("add(2)后, 当前 arraylist --> " + arrayList + "\t 当前 size " + arrayList.size());
arrayList.add(1, 15);
System.out.println("add(1, 15)后, 当前 arraylist --> " + arrayList + "\t 当前 size " + arrayList.size());
System.out.println("============== 华丽分割线 ==============");
/* 一行添加元素 */
ArrayList<Integer> arrayList01 = new ArrayList<Integer>() {{
add(30);
add(40);
}};
LinkedList<Integer> linkedList = new LinkedList<Integer>() {{
add(50);
add(100);
}};
boolean flag = arrayList.addAll(arrayList01);
System.out.println("addAll(arrayList01)后, 当前 arraylist --> " + arrayList + "\t 当前 size " + arrayList.size() + "\t 是否添加成功" + flag);
boolean flag01 = arrayList.addAll(new ArrayList<>());
System.out.println("addAll(new ArrayList<>())后, 当前 arraylist --> " + arrayList + "\t 当前 size " + arrayList.size() + "\t 是否添加成功" + flag01);
boolean linkecFlag = arrayList.addAll(linkedList);
System.out.println("addAll(linkedList)后, 当前 arraylist --> " + arrayList + "\t 当前 size " + arrayList.size() + "\t 是否添加成功" + linkecFlag);
boolean linkecFlag01 = arrayList.addAll(new LinkedList<>());
System.out.println("addAll(new LinkedList<>())后, 当前 arraylist --> " + arrayList + "\t 当前 size " + arrayList.size() + "\t 是否添加成功" + linkecFlag01);
System.out.println("=============== 注意:要么都使用泛型,要么都不用 ==============");
ArrayList nonGeneric = new ArrayList() {
{
add("a50");
add(new Object());
}
};
arrayList.addAll(nonGeneric);
System.out.println("addAll() 添加没有使用泛型的 ArrayList, 当前 arraylist --> " + arrayList + "\t 当前 size " + arrayList.size());
}
}
4.2 获取 get(int index)
- 根据下标获取列表的值,但不移除
public E get(int index) {
/* 检查 index 范围,>= 报错,负数数组自身就会报错 */
rangeCheck(index);
return elementData(index);
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/* 返回值之前,因为泛型擦除的原因,需要进行一次强转 */
E elementData(int index) {
return (E) elementData[index];
}
- 代码演示
package cn.cerish.container.collection.arrayList;
import java.util.ArrayList;
public class GetTest {
public static void main(String[] args) {
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(10);
arrayList.add(20);
arrayList.add(30);
System.out.println("当前 ArrayList :" + arrayList);
System.out.println("获取列表第一个值(下标从0开始): " + arrayList.get(0));
System.out.println("获取列表第二个值(下标从0开始): " + arrayList.get(1));
System.out.println("获取列表第三个值(下标从0开始): " + arrayList.get(2));
System.out.println("获取列表第四个值(下标从0开始),没有,那就报错: " + arrayList.get(3));
}
}
4.3 删除 remove
4.3.1 remove(int index)
- 移除指定下标的元素
public E remove(int index) {
/* 检查 index 范围 */
rangeCheck(index);
/* 修改次数++ */
modCount++;
/* 获取指定位置的元素 */
E oldValue = elementData(index);
/* 获得需要移动的个数,index后面的元素往前移一位,最后一位置为null,方便GC */
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
/* 返回旧值 */
return oldValue;
}
4.3.2 remove(Object o)
- 删除指定的元素
public boolean remove(Object o) {
/* 区别 o 是否为 null,避免空指针异常 */
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
/* 没有找到,返回false */
return false;
}
/* 移除函数 fastRemove(index); */
private void fastRemove(int index) {
/* 修改次数++,将index后的元素左移一位,最后一位置为null */
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
4.3.3 remove(Collection<?> c)
- 删除 集合 c 中共同的元素,有删除成功的返回 true,否则为 false
public boolean removeAll(Collection<?> c) {
/* 非空检查 */
Objects.requireNonNull(c);
return batchRemove(c, false);
}
/* batchRemove 批量移除函数 */
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
/* for循环取出 elementData[r],判断 c 集合是否含有该元素,为true不做操作,为false 记录进 elementData[w++],即从0开始记录,后面的会被删除 */
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
/* r != size 即抛出过错误,需要将未执行 contains() 的元素加入已比对的 w 身后 */
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
/* w != size 代表数据有被移除,将 w 后的个数(即移除个数)置为null,方便GC */
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
/* 可以看到,有移除的元素才返回 true,否则为 false */
return modified;
}
4.3.4 boolean retainAll(Collection<?> c)
- 移除不在集合 c 内的元素
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
/* 这里第二个参数为 true,刚好跟removeAll()的 false 相反 */
return batchRemove(c, true);
}
4.3.5 removeIf(Predicate<? super E> filter)
- 移除符合条件的元素
public boolean removeIf(Predicate<? super E> filter) {
/* 检查过滤器不能为null */
Objects.requireNonNull(filter);
// figure out which elements are to be removed
// any exception thrown from the filter predicate at this stage
// will leave the collection unmodified
int removeCount = 0;
final BitSet removeSet = new BitSet(size);
final int expectedModCount = modCount;
final int size = this.size;
/* for循环逐个元素验证符合 filter 条件 */
for (int i=0; modCount == expectedModCount && i < size; i++) {
@SuppressWarnings("unchecked")
final E element = (E) elementData[i];
/* 满足条件的记录下标 */
if (filter.test(element)) {
removeSet.set(i);
removeCount++;
}
}
/* 不相等则表示 modCount被修改过,抛出错误 */
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
// shift surviving elements left over the spaces left by removed elements
/* removeCount > 0 即有移除的元素,才有必要进行移位赋值 */
final boolean anyToRemove = removeCount > 0;
if (anyToRemove) {
/* 这段代码的例子解释,源列表[0,1,2,3,4],欲删除的位置为1,3,即removeSet下标1,3为true
* 1. 当 i =0, removeSet.nextClearBit(i); 返回0,当 i = 1,返回2,elementData[1] = elementData[2]; 即列表变成[0,2,2,3,4]
* 2. 照此推理, 列表最后变成[0,2,4,3,4] 可自行debugger验证
*/
final int newSize = size - removeCount;
for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
/* nextClearBit() 返回下一个为 false 的下标 */
i = removeSet.nextClearBit(i);
elementData[j] = elementData[i];
}
/* 将后面的元素置空(移除&移位后空出来的位置),方便GC */
for (int k=newSize; k < size; k++) {
elementData[k] = null; // Let gc do its work
}
this.size = newSize;
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
return anyToRemove;
}
4.3.6 小总结
- E remove(int index):移除指定位置的元素,返回被移除的元素
- boolean remove(Object o):找到删除返回true,否则返回false
- boolean removeAll(Collection<?> c):移除与 集合 c 有相同的元素,如果有,返回true,否则返回 false
- boolean retainAll(Collection<?> c):移除与 集合 c 没有交集的元素,如果有,返回true,否则返回 false
- boolean removeIf(Predicate<? super E> filter):移除符合条件的元素,如果有,返回true,否则返回 false
import java.util.ArrayList;
import java.util.BitSet;
public class RemoveTest {
public static void main(String[] args) {
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(10);
arrayList.add(20);
arrayList.add(30);
arrayList.add(40);
arrayList.add(50);
arrayList.add(60);
System.out.println("now arraylist: " + arrayList);
Integer remove0 = arrayList.remove(0);
System.out.println("移除的 0 位元素:" + remove0 + "\t now arraylist: " + arrayList);
System.out.println("==========================================");
/* 这里如果要删除30这个元素,需要传入Integer对象,否则会被当做下标处理 */
Integer integer30 = new Integer(30);
boolean remove30 = arrayList.remove(integer30);
System.out.println("移除指定的元素:30 --> " + remove30 + "\t now arraylist: " + arrayList);
Integer integer300 = new Integer(300);
boolean remove300 = arrayList.remove(integer300);
System.out.println("移除指定的元素: 300 --> " + remove300 + "\t now arraylist: " + arrayList);
System.out.println("==========================================");
ArrayList<Integer> arrayList01 = new ArrayList<>();
arrayList01.add(50);
arrayList01.add(80);
boolean flag = arrayList.removeAll(arrayList01);
System.out.println("移除集合中相同的元素: " + flag + "\t now arraylist: " + arrayList);
/* 没有相同元素的返回 false */
boolean flag01 = arrayList.removeAll(new ArrayList<Integer>(){{add(100);add(300);}});
System.out.println("移除集合中相同的元素: " + flag01 + "\t now arraylist: " + arrayList);
System.out.println("==========================================");
System.out.println("第一次移除大于50的元素: " + (arrayList.removeIf(it -> it > 50) + "\t now arraylist: " + arrayList));
System.out.println("第二次移除大于50的元素: " + (arrayList.removeIf(it -> it > 50) + "\t now arraylist: " + arrayList));
}
}
5 总结
- add() 主要分为两种,指定位置(没有指定默认最末尾 size)添加单个元素与集合(多个元素)
- get() 用于获取指定位置的元素,内部使用了泛型强转,所以在使用的时候不用进行强转。
- remove 分为五种,移除指定位置,移除指定元素,移除指定集合具有 相同 or 不相同 的元素,移除符合指定条件的元素
- 避免篇幅过长,下一篇见。