List是作为日常开发中常用到的一个接口,ArrayList是List的实现之一。
ArrayList 继承了AbstractList类(还实现了RandomAccess, Cloneable,ava.io.Serializable等接口)。它的存储
结构是数组,有一个 int类型的size field作为存储数组对象的索引。
对于List接口我们一般常用到的方法如下:
add,set,get,remove(int index),remove(E value),sort,toArray等
注:使用空的构造函数生成的ArrayList中存储数据的数组的初始长度为10
add方法:该方法有2个实现,分别如下
public boolean add(E e) {
/**
* size+1为添加后的数组索引,用该值去计算当前数组是否能够继续存储需要
* 添加的值,如果数组长度不够的话,将会生成一个新的的数组(长度为原来数组的1.5倍,这个是jdk1.8的实现)
**/
ensureCapacityInternal(size + 1);
//elementData就是存放数据的数组
elementData[size++] = e;
return true;
}
public void add(int index, E element) {
//检查 index是否是否超过 size或者小于0,如果是,则跑出异常
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
/**
* 这个方法是native的(JNI)
* Object src : 原数组
* int srcPos : 从元数据的起始位置开始
* Object dest :目标数组
* int destPos : 目标数组的开始起始位置
* int length : 要copy的数组的长度
* 具体可以参考 https://www.cnblogs.com/DeepLearing/p/5688555.html
**/
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//覆盖当前位置的数据
elementData[index] = element;
size++;
}
set方法:
public E set(int index, E element) {
//检查index是否合法,不合法则抛出IndexOutOfBoundsException异常
rangeCheck(index);
E oldValue = elementData(index);
//覆盖
elementData[index] = element;
return oldValue;
}
get方法比较简单,就是获取之前会判断一下传入参数是否越界,然后在放回数据之前做一个泛型的类型转换
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
remove方法
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;
}
/**
* 这个方法多做了一个查找的动作,移除的时候调用了内部的 fastRemove方法
* fastRemove 方法和remove()方式类似,也是用了 System.arraycop
**/
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 remove method that skips bounds checking and does not
* return the value removed.
*/
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
}
sort方法,需要传入一个Comparator接口的实现,在ArrayList的内部是调用了Arrays.sort方法,这边有一个需要注意的是,
因为ArrayList是非线程安全的,所以在多线程环境下sort方法有可能会抛出 ConcurrentModificationException异常。所以多线程环境可以使用 Collections.synchronizedList(new ArrayList()); 或者是用java.util.concurrent(java8)包下的集合类
@Override
@SuppressWarnings("unchecked")
public void sort(Comparator<? super E> c) {
//记录当前操作次数的标记值
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
//如果在排序过程中有其他线程操作了这个list将会跑出异常
//注:modCount这个成员变量在除了add,get,set等操作外的每一次操作ArrayList的时候都会增加一次
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
toArray 转化为数组,这个方法也有2个实现,关键的是他不是直接放回内部的存储数据的数组,而是做了一次值拷贝
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;
}
补充:
indexOf,lastIndexOf这2个方法也很常用,这2个方法的实现其实就是将ArrayList中的数组遍历一次,不过indexOf是从数组开头开始遍历,而lastIndexOf是从当前数组下标索引size-1的位置开始往前查找。
注:该内容基于jdk1.8