简介
ArrayList提供了List ADT的一种可增长数组的实现,底层基于数组实现,对get/set的调用花费常数时间,但插入/删除项则代价昂贵。
构造函数
这里面涉及到三个数组,EMPTY_ELEMENTDATA
,DEFAULTCAPACITY_EMPTY_ELEMENTDATA
,elementData
,其中前两个数组主要用于在初始化以作区分,详见构造函数的区别。其中elementData
是数据实际存储的数组,它使用transient关键字创建。
在这儿有三种构造函数:
MyArrayList(int initialCapacity)
若传参为0,elementData
会指向EMPTY_ELEMENTDATA
(未指定长度),若>0则创建数组new Object[initialCapacity]
,<0则抛出异常。MyArrayList()
采用这种构造函数创建ArrayList,elementData
会指向DEFAULTCAPACITY_EMPTY_ELEMENTDATA
(未指定长度)MyArrayList(Collection<? extends E> c)
以数组的形式建立
增
采用MyArrayList(int initialCapacity)
、MyArrayList()
构造的数组在调用add
方法时,处理逻辑会不一样,依次调用ensureCapacityInternal()(它调用后两者,故不做介绍),calculateCapacity(),ensureExplicitCapacity()三个函数
calculateCapacity()
:若是以MyArrayList()
构造函数创建的ArrayList,首次赋值按照Math.max(DEFAULT_CAPACITY, minCapacity)的方式增加数组容量,也就是说<DEFAULT_CAPACITY时,取DEFAULT_CAPACITY;ensureExplicitCapacity
:针对MyArrayList()
、MyArrayList(int initialCapacity)
当容量不够时,均按照1.5倍old容量进行扩容。
流程图如下:
当数组length快溢出时,执行以下的逻辑
/**
* 有些VM会用8的长度来存储数组_length字段,记录数组长度,只需要去读_length字段就可以了
* 如果值比这个还大,就会溢出(当一个整数一直++,超出最大值后会变成负值)
*/
private static final int 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;
}
删除
- 单个删除
//按位置删除
public E remove(int index) {
rangeCheck(index); //位置有可能为负,或越界
modCount++;
E oldValue = elementData(index);
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;
}
//根据index返回elementData元素
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
//按对象删除
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;
}
- 批量删除
//由于已经remove(Object o)调用前已经能确保该元素位于elementData[]中,故:
//1.无需判断index合法性;
//2.无需返回删除的元素(删除的元素调用方是知晓的)
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
}
//按数组删除,也就是如果elementData[]里存在c的元素,则删除
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
//按数组保留,也就是如果elementData[]里存在c的元素,则保留
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}
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.
// 避免出现Exception(什么时候会出Exception???)的时候好将原数组后续的元素保留下来
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
//由于elementData中元素被删除了一部分,因此后续的元素需要补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;
}
}
return modified;
}
修改
直接替换元素,比较高效的操作
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
查
直接根据index获取元素,相比与LinkedList而言非常高效
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
清空
跟删除罗辑一直,GC会自动完成优化
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
包含
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
//对象在没有被分配空间时是不能调用任何方法的
//故此处不能用.equal(..)方法,否则会报java.lang.NullPointerException空指针异常
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 isEmpty() {
return size == 0;
}
迭代器
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
//只要实例化了Itr对象,那么expectedModCount就会保留当时的modCount,如果非此Itr对象对elementData
//进行了修改,那么modCount会改变,这样expectedModCount != modCount抛异常
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
//ArrayList.this.remove()指调用外部类ArrayList的remove()方法
Object[] elementData = ArrayList.this.elementData;
//再次判断是否越界,害怕我们在这里的操作时,有异步线程修改了List
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();
}
}
//此方法的解释见下文
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
有两个遍历元素的方法,位于Iterator中的forEachRemaining(Consumer<? super E> consumer),以及集合本身的forEach(Consumer<? super E> consumer),代码如下:
/*
* 他们均用于函数式编程中,详见Java函数式编程相关知识点
* 区别如下:
* forEachRemaining本质是while(iterator.hasNext()){iterator.next()}操作
* forEach本质是 for (Type e : collection) {...}操作
* 也就是说针对同一个iterator对象,forEachRemaining只能使用一次,第二次使用由于iterator.hasNext()为false,
* 故不会执行;而forEach可以执行多次
*/
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
举例如下:
List<String> stringList = new ArrayList<>();
stringList.add("a"); stringList.add("b"); stringList.add("c");
Consumer<String> consumer = System.out::println;
Iterator<String> iterator = stringList.iterator();
iterator.forEachRemaining(consumer);
iterator.forEachRemaining(consumer); //第二次不会有输出
stringList.forEach(consumer);
stringList.forEach(consumer); //第二次依然有输出
注意:还有一个遍历的对象ListItr
,它是Itr
的加强版,拥有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历等。
其它代码
- 收缩,减少ArrayList的存储空间占用
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
- 返回最后一个匹配元素的位置
它是从后往前查找的,返回找到的第1个
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;
}
- 返回视图SubList
public List<E> subList(int fromIndex, int toIndex) {
...
}
List的subList方法并没有创建一个新的List,而是使用了原List的视图,这个视图使用内部类SubList表示。修改其中一个,变更会反应到另一个上面:
- 对父(sourceList)子(subList)List做的非结构性修改(non-structural changes),都会影响到彼此。
- 对子List做结构性修改,操作同样会反映到父List上。
- 对父List做结构性修改,会抛出异常ConcurrentModificationException。
- 分割
分割后方便并行处理,
@Override
public Spliterator<E> spliterator() {
return new ArrayListSpliterator<>(this, 0, -1, 0);
}
/** Index-based split-by-two, lazily initialized Spliterator */
static final class ArrayListSpliterator<E> implements Spliterator<E> {
...
}
- 通过lambda表达式实现元素的删除
public boolean removeIf(Predicate<? super E> filter) {
...
}
- 通过lambda表达式实现元素的替换
// 在所有元素末尾增加"aaa"字符
stringList.replaceAll(x -> x + “aaa”);
public void replaceAll(UnaryOperator<E> operator) {
...
}
在java 1.8中,函数编程的接口如下:Predicate、Consumer、Function、Supplier、UnaryOperator
- 自定义sort规则实现排序
public void sort(Comparator<? super E> c) {
...
}
支持重写Compare方法以自定义的规则实现排序。