集合(一)数组ArrayList
1. 概述
ArrayList基于Object[],支持自动扩容实现的动态数组,日常开发中最常用的集合类。
2. 类图
继承了AbtractList类,实现了RandomAcess、Serializable、Cloneable和List,前三者是标记接口。
ArrayList重写了List的方法,因此继承AbstractList没发挥太多作用。
3. 属性
transient Object[] elementData; // 元素数组
private int size; // 实际存储元素个数
4. 构造方法
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
private static final int DEFAULT_CAPACITY = 10;
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);
}
}
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
// JDK9解决
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
ArrayList为了节省内存,在首次添加元素时,才会真正初始化数组。
EMPTY_ELEMENTDATA从0开始扩容
而DEFAULTCAPACITY_EMPTY_ELEMENTDATA从10开始扩容
5. 添加元素
// 添加元素,在size位置添加
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
// 1.5倍扩容,若不能容纳新增元素,则直接扩至容纳新增元素
// 数组扩容,核心方法
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:
elementData = Arrays.copyOf(elementData, newCapacity);
}
// index位置添加元素
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
// index ~ size-1元素整体后移一位
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
// 添加多个元素,在size位置添加
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
// index位置添加多个元素
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
int numMoved = size - index;
// index ~ size-1元素整体后移size-index位
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
6. 移除元素
// 按索引移除元素
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
// index+1 ~ size-1 整体前移一位
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
// 移除对象
public boolean remove(Object o) {
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;
}
}
return false;
}
// 快速移除,无需检查数组范围
private void fastRemove(int index) {
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
}
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
// 求交集或差集,都是子集,数组原地替换,剩余的位置指向null
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
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;
}
}
return modified;
}
// elementData与c的交集
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}
// elementData与c的差集
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
// 清空数组,数组设置为null,size设置为0
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
7. 查找元素
// 顺序遍历查找元素
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
// 是否包含元素
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
// 反序遍历查找元素
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
E elementData(int index) {
return (E) elementData[index];
}
// 根据索引index获取元素
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
// 设置索引index的值
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
// 拷贝一份数组
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
// 拷贝一份泛型数组
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;
}
8. 序列化和反序列化
序列化:将对象转换为字节
反序列化:将字节转换为对象
应用场景:网络传输和数据存储
transient修饰不会进行序列化和反序列化
没有定义serialVersionUID值, Java编译器会对jvm中该类的Class文件进行摘要算法生成一个
此时若修改类名,成员变量名会导致生成的serialVersionUID不同,导致反序列化失败。
因此建议固定一个serialVersionUID值
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// 1. 写入非静态,非transient属性
int expectedModCount = modCount;
s.defaultWriteObject();
// 2. 写入数组大小
// ArrayList中size非静态,非transient,在一次被写入,重复写入size,有点奇怪
// 在readObject() s.readInt(); 是忽略的
s.writeInt(size);
// 3. 顺序遍历,逐个序列化
// ArrayList elmentData由transient修饰,按照实际大小,序列化数组元素
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA;
// 1. 读取非静态,非transient属性
s.defaultReadObject();
// 2. 读取数组大小
s.readInt(); // ignored
// 3. 顺序遍历,逐个反ß序列化
// ArrayList elmentData由transient修饰,按照实际大小,反序列化数组元素
// capacity和size相等
if (size > 0) {
// be like clone(), allocate array based upon size not capacity
int capacity = calculateCapacity(elementData, size);
SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
ensureCapacityInternal(size);
Object[] a = elementData;
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
a[i] = s.readObject();
}
}
}
HashMap必须重写writeObject()、readObject(),因为序列化会导致字节流在不同的JVM中传输,而Entry的存放位置根据Ky的hash值计算,对于同一个key,不同的JVM会导致计算出来hash值不同,从而违背反序列化的基本要求——反序列化的对象与序列化之前的对象是一致的。
一般ArrayList的数据容量大于实际元素数量,ArrayList重写writeObject()、readObject(),,为了避免序列化没有元素的数组
序列化和clone()都产生新对象
9. 内部类
// AbstractList.Itr更优化的版本
private class Itr implements Iterator<E>;
// AbstractList.ListItr更优化的版本
private class ListItr extends Itr implements ListIterator<E>;
// 原数组切片
private class SubList extends AbstractList<E> implements RandomAccess;
// 数组中可以分割的迭代器
static final class ArrayListSpliterator<E> implements Spliterator<E>
10. 小结
ArrayList随机访问快,随机访问for i比顺序访问for each更快
LinkedList增加、删除元素快。
ArrayList支持自动扩容
Redis String底层数据结构类似于Java ArrayList