目录
前言:
版本:jdk: 1.8
ArrayList是以数组方式实现的。
优点:
根据下标进行操作时很快,可以立即定位元素的位置。
缺点:
直接查询元素时会比较慢,无法立即定位元素的位置,必须循环整个数组。
类说明:
public class ArrayList<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
1. 实现RandomAccess接口
RandomAccess接口是一个标记接口,能支持快速随机访问
//使用时机:Collections的binarySearch方法,会判断是否实现了这个接口,从而选择不同的遍历方法
//实现了RandomAccess接口,表示支持随机访问,就使用下标遍历
//未实现RandomAccess接口,则使用迭代器遍历
public static <T>
int binarySearch(List<? extends Comparable<? super T>> list, T key) {
if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
return Collections.indexedBinarySearch(list, key);
else
return Collections.iteratorBinarySearch(list, key);
}
2. 实现Cloneable接口
Cloneable是标记型的接口,它们内部都没有方法和属性,实现 Cloneable来表示该对象能被克隆,能使用Object.clone()方法。如果没有实现 Cloneable的类对象调用clone()就会抛出CloneNotSupportedException。
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
3.实现Serializable接口
是一个标记接口,实现该接口的类,可以被序列化。
成员变量:
//默认容量
private static final int DEFAULT_CAPACITY = 10;
//空的元素数据,当初始化容量=0时使用
private static final Object[] EMPTY_ELEMENTDATA = {};
//默认的空元素数据,当使用无参构造函数时使用
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 问题:
* 为什么存储两个空的数组 EMPTY_ELEMENTDATA 和 DEFAULTCAPACITY_EMPTY_ELEMENTDATA ?
* 为了区分使用者是故意初始化容量为0(EMPTY_ELEMENTDATA ),
* 还是没有初始化容量的(DEFAULTCAPACITY_EMPTY_ELEMENTDATA )。
* 当没有初始化容量时,才会使用容量的默认值DEFAULT_CAPACITY=10,
* 如果故意初始化为0,就不会再使用默认值了
*/
//存放数据的数组
//使用transient,防止其被序列化。
transient Object[] elementData;
/**
* 问题:为什么elementData要使用transient修饰来防止其序列化
* 原因:因为elementData是一个缓存数组,里面会有空的元素,在进行序列化的时候,只需要序列化实际 * 存在的元素,空的元素就不需要再进行序列化了,所以elementData不会直接序列化,而是取出其中的
* 实际元素,再进行序列化
*/
//当前的数据条数
private int size;
//最大的数组大小,数组需要8 bytes去存储它自己的大小,所以-8
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//从AbstractList中继承的属性,指的是此列表在结构上被修改的次数
//结构修改:改变结构大小的修改列表,或者以某种方式对其进行扰动,使迭代可能会产生错误的结果。
//如add()、remove()、clear()、trimToSize()、sort()等操作时,会执行modCount++;
protected transient int modCount = 0;
构造方法:
//1. 空的构造方法,会使用默认值作为数组初始的大小
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//2. 初始化集合大小
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);
}
}
//3.使用Collection来初始化ArrayList
public ArrayList(Collection<? extends E> c) {
elementData = 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;
}
}
常用方法:
扩容机制:
扩容时机 : 添加数据时进行扩容。
//第一步:判断是否要使用默认容量
private void ensureCapacityInternal(int minCapacity) {
//判断是否调用空的构造函数构造,如果是,就使用默认容量10
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
//第二步:判断扩容后的容量是否>当前容量,如果大于,才进行扩容
private void ensureExplicitCapacity(int minCapacity) {
//修改数+1,为快速失败机制使用
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//第三步:得到扩容后的新长度,执行扩容方法
private void grow(int minCapacity) {
// 得到旧容量
int oldCapacity = elementData.length;
// 新的容量 = 旧容量 + 旧容量>>1,即旧容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//新的容量 < 预计要扩容的容量,则 使用minCapacity
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//检查新的容量 是否最大容量,如果是,则再进行处理
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//扩容,利用Arrays的复制方法,生成一个新的数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
添加数据:
//1. 插入元素
public boolean add(E e) {
//扩容
ensureCapacityInternal(size + 1);
//向下一位插入指定元素
elementData[size++] = e;
return true;
}
//2.向指定位置插入元素
public void add(int index, E element) {
//检查下标index是否越界 : 是否为负值;是否大于size
rangeCheckForAdd(index);
//扩容
ensureCapacityInternal(size + 1);
//从要插入的位置,数组整体右移,腾出一个位置出来
System.arraycopy(elementData, index, elementData, index + 1,size - index);
//向指定位置插入元素
elementData[index] = element;
size++;
}
//3. 插入集合元素
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
//扩容
ensureCapacityInternal(size + numNew);
//将新插入的集合数组a,整体复制到当前数组elementData中
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
4.在指定位置插入集合元素
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;
if (numMoved > 0)
//如果要插入集合,则首先移动数组,腾出问题
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
//将新集合,得到本数组elementData的指定下标处。
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
删除元素:
//1.移除指定下标处的元素。
public E remove(int index) {
//检查下标是否越界
rangeCheck(index);
//修改次数+1
modCount++;
//得到当前数组下标处理的元素
E oldValue = elementData(index);
//判断要删除该元素,需要移动几个元素。
//如果是0,表示是最后一位,不需要移动,直接置为null即可。
int numMoved = size - index - 1;
if (numMoved > 0)
//数组整体左移
System.arraycopy(elementData, index+1, elementData, index, numMoved);
//设为null,让GC时将其回收
elementData[--size] = null;
return oldValue;
}
//2.移除指定的元素
//注意:如果有多个该元素,只会移除第一个存放进去的
public boolean remove(Object o) {
//判断是否为null,为null,使用 == 比较。不为null,使用equals比较是否相等
//遍历整体数组,进行比较
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;
}
//3. 移除集合元素
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
//批量移除方法
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
//r : 循环次数= size ; w:批量删除后剩余元素的个数
int r = 0, w = 0;
boolean modified = false;
try {
//complement = false时:
//保存的是c中不存在的元素,放在elementData中的前w位
//循环结束后 ,[0,w]:就是要剩下的元素,[w,size]为c中的元素
//complement = true时:
//保存的是c中存在的元素,放在elementData中的前w位
//循环结束后 ,[0,w]:为c中的元素,[w,size]为其他元素
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.
//处理异常情况。正常情况下r==size,当r!=size时,应该是出现了异常情况
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// 将[w,size]之间的数据转为null
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
修改元素:
1)修改元素
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
//直接修改指定下标处理的元素
elementData[index] = element;
return oldValue;
}
查询元素:
1)查询指定下标处理的元素
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
2)查询指定元素的下标(元素第一次出现的位置的下标)
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;
}
3)查询指定元素的下标(元素最后一次出现的位置的下标)
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;
}
其他方法:
1) 得到集合中数据的数量
public int size() {
return size;
}
2)判断集合中是否有元素
public boolean isEmpty() {
return size == 0;
}
3)去掉集合中的空元素,类以于String的trim方法,一个去空元素,一个去空格,这样可以节省空间
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
4)清空集合中的所有数据,让GC回收
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
5)截取指定下标区间内的数据,得到子集合。对子集合的修改也会影响到父类
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
遍历集合:
1. 下标遍历
2. 迭代器遍历
采用ArrayList对随机访问比较快,而for循环中的get()方法,采用的即是随机访问的方法,因此在ArrayList里,for循环较快。
List<String> list = new ArrayList<>(20);
list.add("1");
list.add("2");
list.add("3");
//1、下标遍历
System.out.println("index-------------");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
//2、迭代器遍历
//增强for循环
System.out.println("for-------------");
for (String s : list) {
System.out.println(s);
}
//while循环,正向遍历
System.out.println("iterator-------------");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("listIterator-------------");
//while循环,使用listIterator,进行反向遍历
ListIterator<String> listIterator = list.listIterator(list.size());
while (listIterator.hasPrevious()){
System.out.println(listIterator.previous());
}
内部类:
SubList类:
//子集合
//内部会操作原父集合,所以会它的修改也会影响父类集合
private class SubList extends AbstractList<E> implements RandomAccess {
//原父集合
private final AbstractList<E> parent;
private final int parentOffset;
private final int offset;
int size;
SubList(AbstractList<E> parent,
int offset, int fromIndex, int toIndex) {
this.parent = parent;
this.parentOffset = fromIndex;
this.offset = offset + fromIndex;
this.size = toIndex - fromIndex;
this.modCount = ArrayList.this.modCount;
}
}
Itr类:
//迭代器【可以删除元素】
private class Itr implements Iterator<E> {
//游标,即下一个元素的下标
int cursor;
//返回的最后一个元素的索引;如果没有,就返回-1
int lastRet = -1;
//期望修改的数量,用于快速失败机制。在遍历时可以使用remove删除元素。
//每次遍历前会检查是否一致,如果不一致,则抛出ConcurrentModificationException异常
int expectedModCount = modCount;
//每次循环前,先调用该方法判断是否还有元素
public boolean hasNext() {
return cursor != size;
}
//得到下个元素
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
//移除当前遍历的元素
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
ListItr类:
/**
* 继承Itr,可以进行向左遍历,得到上一个元素的值
*/
private class ListItr extends Itr implements ListIterator<E> {
//指定遍历时的初始下标
ListItr(int index) {
super();
cursor = index;
}
public boolean hasPrevious() {
return cursor != 0;
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor - 1;
}
@SuppressWarnings("unchecked")
public E previous() {
checkForComodification();
int i = cursor - 1;
if (i < 0)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i;
return (E) elementData[lastRet = i];
}
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.set(lastRet, e);
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public void add(E e) {
checkForComodification();
try {
int i = cursor;
ArrayList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
ArrayListSpliterator类
用于拆分ArrayList,然后批量处理这些集合,提高集合的处理效率。