一直在理论层面学习,今天来解析一下ArrayList的源码(JDK1.8):
首先ArrayList是放在java.util包下的,是一个最简单的线性结构
打开之后发现
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
ArrayList继承了AbstractList<E>抽象类(从名字看出的抽象类)
然后分别实现了List<E>, RandomAccess, Cloneable, java.io.Serializable接口,我们可以看出List<E>和AbstractList<E>接口是有上下级关系的,本着追根溯源的精神,我们把其他三个接口也看看源码长什么样
RandomAccess:从名字我们可以看出这是随机读取接口,实现了它应该就标记着该类支持随机存取,那么我们可以就来看一下它的源码:
public interface RandomAccess {
}
这是一个空接口,看来又是一个标记接口,那么标记了它有什么用呢?
答案是在Collections下的binarySearch()方法以及其他查找方法中如果发现实现了RandomAccess接口就会进行二分查找,不实现就用迭代器进行查找,简单来说就是标记了RandomAccess方便了对其进行查找的方法进行优化.
Cloneable:我们知道这是一个典型的标记接口,标记该类可以克隆,能使用Object.clone()方法。如果没有实现 Cloneable的类对象调用clone()就会抛出CloneNotSupportedException。那么我们猜测它也是一个空接口,我们来查看一下源码:
public interface Cloneable {
}
果然也是一个空接口,接下来我们查看一下Serializable接口:
Serializable:这也是一个典型的标记接口,实现了这个接口标记该类可以序列化,我们仍然猜测这是一个空接口:
public interface Serializable {
}
果然不出所料,从上面我们知道了ArrayList实现了三个接口以表示ArrayList是支持随机访问的,可以克隆,可以序列化的.接下来我们来看看与它密切相关的List<E>,和Abstract<E>:
我们先来看List<E>接口,因为我们知道它一定比Abstract<E>层级高,并且很可能Abstract<E>继承自它:
public interface List<E> extends Collection<E> {
int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
<T> T[] toArray(T[] a);
boolean add(E e);
boolean remove(Object o);
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean addAll(int index, Collection<? extends E> c);
boolean removeAll(Collection<?> c);
boolean retainAll(Collection<?> c);
default void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
final ListIterator<E> li = this.listIterator();
while (li.hasNext()) {
li.set(operator.apply(li.next()));
}
}
default void sort(Comparator<? super E> c) {
Object[] a = this.toArray();
Arrays.sort(a, (Comparator) c);
ListIterator<E> i = this.listIterator();
for (Object e : a) {
i.next();
i.set((E) e);
}
}
void clear();
boolean equals(Object o);
int hashCode();
E get(int index);
E set(int index, E element);
void add(int index, E element);
E remove(int index);
int indexOf(Object o);
int lastIndexOf(Object o);
ListIterator<E> listIterator();
ListIterator<E> listIterator(int index);
List<E> subList(int fromIndex, int toIndex);
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, Spliterator.ORDERED);
}
}
我们看到List<E>接口中定义了List的常用的方法和一些JDK8可以在接口中定义的默认方法,从名字我们很容易知道它们所代表的意思,下面我们来看一下Abstract<E>接口:
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
protected AbstractList() {
}
public boolean add(E e) {
add(size(), e);
return true;
}
abstract public E get(int index);
public E set(int index, E element) {
throw new UnsupportedOperationException();
}
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
public E remove(int index) {
throw new UnsupportedOperationException();
}
public int indexOf(Object o) {
ListIterator<E> it = listIterator();
if (o==null) {
while (it.hasNext())
if (it.next()==null)
return it.previousIndex();
} else {
while (it.hasNext())
if (o.equals(it.next()))
return it.previousIndex();
}
return -1;
}
public int lastIndexOf(Object o) {
ListIterator<E> it = listIterator(size());
if (o==null) {
while (it.hasPrevious())
if (it.previous()==null)
return it.nextIndex();
} else {
while (it.hasPrevious())
if (o.equals(it.previous()))
return it.nextIndex();
}
return -1;
}
public void clear() {
removeRange(0, size());
}
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
boolean modified = false;
for (E e : c) {
add(index++, e);
modified = true;
}
return modified;
}
public Iterator<E> iterator() {
return new Itr();
}
public ListIterator<E> listIterator() {
return listIterator(0);
}
public ListIterator<E> listIterator(final int index) {
rangeCheckForAdd(index);
return new ListItr(index);
}
public List<E> subList(int fromIndex, int toIndex) {
return (this instanceof RandomAccess ?
new RandomAccessSubList<>(this, fromIndex, toIndex) :
new SubList<>(this, fromIndex, toIndex));
}
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof List))
return false;
ListIterator<E> e1 = listIterator();
ListIterator<?> e2 = ((List<?>) o).listIterator();
while (e1.hasNext() && e2.hasNext()) {
E o1 = e1.next();
Object o2 = e2.next();
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
return !(e1.hasNext() || e2.hasNext());
}
public int hashCode() {
int hashCode = 1;
for (E e : this)
hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
return hashCode;
}
protected void removeRange(int fromIndex, int toIndex) {
ListIterator<E> it = listIterator(fromIndex);
for (int i=0, n=toIndex-fromIndex; i<n; i++) {
it.next();
it.remove();
}
}
protected transient int modCount = 0;
private void rangeCheckForAdd(int index) {
if (index < 0 || index > size())
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size();
}
}
里面还包含了两个内部类Itr和ListItr,由于篇幅就不黏贴了,我们可以看到AbstractList类对List<E>中的大部分方法进行了实现,其中我只发现了一个abstract方法get方法,也因为这个方法是abstract的所以类要加上abstract.
接下来我们真正来看ArrayList的源码:
private static final long serialVersionUID = 8683452581122892189L;
序列化编号,没什么好说的,控制反序列化与序列化一致的
private static final int DEFAULT_CAPACITY = 10;
从名字我们可以看出这是声明创建一个ArrayList的默认容量是10
private static final Object[] EMPTY_ELEMENTDATA = {};
这条非常重要!定义了一个空集合,从侧面反映了ArrayList是用集合来存储数据的!
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
这里又声明了一个默认容量时的空数组,暂时不明白有什么用,等看到下面是否有发现吧.
transient Object[] elementData; // non-private to simplify nested class access
这里是真正的ArrayList的底层数据接口,就是Object[]数组,用transient修饰,嗯.
嗯?不对!transient修饰的变量不可被序列化吗?ArrayList不是实现了Serializable接口吗?再说了,ArrayList不可被序列化可还行?
在经过一番百度之后,对transient有了更深的认识:
transient关键字的作用:在采用Java默认的序列化机制的时候,被该关键字修饰的属性不会被序列化。
原来:
Transient只保证在使用java默认序列化机制的时候不序列化它修饰的变量,但对用户自定义的序列化是没有效果的,我们看ArrayList的最后就会发现ArrayList实现了
private void writeObject(java.io.ObjectOutputStream s)
和
private void readObject(java.io.ObjectInputStream s)
这两个方法,便可以用自己的方式进行序列化,那么ArrayList为什么不用java默认的序列化机制呢?
原因在于ArrayLsit是一个缓冲结构,如果ArrayList当前容量为10,但你只存储了6个数据,那么剩余的4个还用不用存储呢?显然,java默认的序列化机制将会一一进行序列化,而自定义序列化过程将会优化这个过程.
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
终于读到了ArrayList的构造器,在无参进行构造的时候elementData被赋值为默认容量的空集合(DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
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);
}
}
这里用一个int参数来进行构造,当值大于0的时候构建相等值的Object[]数组,当值等于0的时候elementData = EMPTY_ELEMENTDATA,当值小于0的时候当然抛出异常啦,这里我们仍然没有发现EMPTY_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)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
用另外一个集合对象进行构造ArrayList,我们看到toArray()方法很自然地想到会返回一个数组,但是真的是这样吗?我们来验证一下:
我们先来创建一个interfaceA:
public interface interfaceA {
Object[] toArray();
}
用一个类去实现它:
public class Impla implements interfaceA{
@Override
public ArrayList[] toArray() {
return new ArrayList[0];
}
}
在IDE中是没有报错的,原来重写父类方法的时候返回方法也是可以用子类的,所以到这里,toArray方法返回的不一定是Object[]数组,而有可能是子类的数组,我们凭直觉感觉这样不好,可是,为什么不好?带着这个疑问我又百度了一波,发现了其中的道理:
如果在Object[]数组中存放的不是Object类型,然后再替换其中一个为Object元素的时候就会出错(向下转型错误,如果数组类型为Object[],那么向里面添加Object的子类是可以的),所以在下一步还是要要判断一下返回的Object[]数组中真的是Object实例吗?(这里参考的文章是https://blog.csdn.net/gulu_gulu_jp/article/details/51457492)
接下来就判断如果是Object[]实例的话就不变,如果不是的话用Arrays的copyOf进行copy回一个真正的Object[]数组,如果传入的集合长度为0的话就仍然elementData = EMPTY_ELEMENTDATA,但是我们还是没有发现EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA区别开有什么好处.
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
修改这个ArrayList实例的容量是列表的当前大小。 应用程序可以使用此操作来最小化ArrayList实例的存储。
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
如果需要,增加此 ArrayList实例的容量,以确保它至少可以容纳最小容量参数指定的元素数。
参数
minCapacity - 所需的最小容量
Arraylist在add时是会根据大小自动扩容的,这个方法的意义在于你已知要存储的容量大小,直接设定之后直接扩容到你需要的大小,不需要中途几次扩容.
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
定义了ArrayList的最大容量为int的最大值减8,至于减8?少了几个而已,没必要深究
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);
}
进行扩容的方法,可以看出一次以1.5倍速增长
public int size() {
return size;
}
返回size大小
public boolean isEmpty() {
return 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++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -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;
}
倒着找索引
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);
}
}
Clone方法,注意modCount改成0
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
返回Object[]数组
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index];
}
返回指定位置的元素
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
设置指定的元素
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
添加一个元素,当容量不够时会自动扩容
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
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;
}
删除指定位置的元素,与上同理
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
}
Remove实际上是利用数组复制把要删除的那个元素覆盖没了
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
Clear会把每个元素设置成null,GC(垃圾收集器)会释放它们
其他的方法不是很重要,有需要继续研究~!
另外,ArrayList不是同步的,所以在并发访问的时候,如果在迭代的同时有其他线程修改了ArrayList,迭代器会报ConcurrentModificationException异常,如果要在不并发环境下使用List,需要给List加个同步锁,List list = Collections.synchronizedList(new ArrayList(...));
Over!