集合之家——ArrayList(源码分析)

这几天面试突然遇到几个ArrayList的问题,有几个还真是搞不出来,觉得自己之前还不够深入吧,于是就自己重新学了一遍集合,并从底层进行了源码分析。这里就先直接从源码开始了。


源码分析


public class ArrayList extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable { 我们先看它的类声明: 继承了AbstractList 实现了List
RandomAccess Cloneable java.io.Serializable接口

怎么说呢,后面三个分别是实现RandomAccess,Cloneable接口(源码里面没有具体的其他东西,估计就是摆给我们看,没有太大用处,不做重点分析,就是提供随机访问,克隆而已,具体的实现还是要看具体代码),还有序列化接口,也不做重点分析,我写了其他的序列化文章了。
我们重点先看看父类AbstractList和List接口
AbstractList=>List=>ArrayList

AbstractList父类

AbstractList是一个抽象类
public abstract class AbstractList extends AbstractCollection implements List
由此可知,它又继承了AbstractCollection,更深一层就是顶层集合Collection,并实现List接口
public boolean add(E e) {
add(size(), e);
return true;
}

abstract public E get(int index);   //根据索引获取泛型E
public E set(int index, E element) //修改元素
public void add(int index, E element)//添加元素
public E remove(int index) //移除元素
public int indexOf(Object o) //根据对象获取索引
public int lastIndexOf(Object o) 	
public void clear()//清空
public boolean addAll//添加所有的

上面这些一般都是自己可以实现的,但是我们看源码其他实现的几个东西。

public Iterator<E> iterator() {
    return new Itr();
}这个·方法马上就new了一个内部类Itr,就是下面这个
 private class Itr implements Iterator<E>这个类无非就是一些迭代器Iterator的实现而已。

public ListIterator listIterator() {
return listIterator(0);
}
public ListIterator listIterator(final int index) {
rangeCheckForAdd(index);
return new ListItr(index);
}
private class ListItr extends Itr implements ListIterator//这也是一个内部类,里面方法没有摘抄过来,大家可以自己去看,
**protected transient int modCount = 0;**这是一个超级超级重要的变量,它是判断当前更改是否合理的判断标准,它记录的是修改次数,来判断是否线程安全
public List subList(int fromIndex, int toIndex) {
return (this instanceof RandomAccess ?
new RandomAccessSubList<>(this, fromIndex, toIndex) :
new SubList<>(this, fromIndex, toIndex));
}
这个东西有嗲意思了,它返回的是一个List,并且有两种可能的返回结果,一种是RandomAccessSubList还有一种是SubList,这两个也是它的内部类
SubList我也只弄了几个常用的:他表示要截取的父list视图
class SubList extends AbstractList {
private final AbstractList l;
private final int offset;
private int size;
SubList(AbstractList list, int fromIndex, int toIndex) {
if (fromIndex < 0)
throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
if (toIndex > list.size())
throw new IndexOutOfBoundsException("toIndex = " + toIndex);
if (fromIndex > toIndex)
throw new IllegalArgumentException(“fromIndex(” + fromIndex +
“) > toIndex(” + toIndex + “)”);
l = list;//接构造方法的AbstractList对象,赋给自己的对象变量
offset = fromIndex;//偏移就是list的某一个索引位置
size = toIndex - fromIndex;//toindex就是末端索引
this.modCount = l.modCount;
}
public E set(int index, E element) //就是调用l的set方法,下面的一样
public E get(int index)
public int size()
public void add(int index, E element)
public E remove(int index)
protected void removeRange(int fromIndex, int toIndex)
public boolean addAll(Collection<? extends E> c)
public boolean addAll(int index, Collection<? extends E> c)
public Iterator iterator() {
return listIterator();
}
public ListIterator listIterator(final int index) {
checkForComodification();
rangeCheckForAdd(index);
return new ListIterator() {
private final ListIterator i = l.listIterator(index+offset);
public boolean hasNext() {
return nextIndex() < size;
}
public E next() {
if (hasNext())
return i.next();
else
throw new NoSuchElementException();
}
public boolean hasPrevious() {
return previousIndex() >= 0;
}
public E previous() {
if (hasPrevious())
return i.previous();
else
throw new NoSuchElementException();
}
public int nextIndex() {
return i.nextIndex() - offset;
}
public int previousIndex() {
return i.previousIndex() - offset;
}
public void remove() {
i.remove();
SubList.this.modCount = l.modCount;
size–;
}
public void set(E e) {
i.set(e);
}
public void add(E e) {
i.add(e);
SubList.this.modCount = l.modCount;
size++;
}
};
}
public List subList(int fromIndex, int toIndex) {
return new SubList<>(this, fromIndex, toIndex);
}
private void rangeCheck(int index)
private void rangeCheckForAdd(int index)
private String outOfBoundsMsg(int index)
private void checkForComodification() {//这个方法非常重要,这个涉及到后面将要讲的failfast机制,和多线程访问安不安全的问题,他其实就是根据当前的modCount和父list的modCount是不是相同的(就是修改次数是不是相同的),来判断这个Sublist类在操作是有没有受其他线程干扰。
if (this.modCount != l.modCount)
throw new ConcurrentModificationException();
}
}
这个SubList方法其实返回的就是父List的一个子引用,也就是说,这个sublist方法得到的子引用做的任何修改,都会影响到原来的父List,也就是说他只是一个视图而已,这里特别要注意。
class RandomAccessSubList extends SubList implements RandomAccess {
RandomAccessSubList(AbstractList list, int fromIndex, int toIndex) {
super(list, fromIndex, toIndex);
}
public List subList(int fromIndex, int toIndex) {
return new RandomAccessSubList<>(this, fromIndex, toIndex);
}
}
这个和上面的sublist是同样的道理了
基本AbstractList到这里就差不多了

List接口

这个类没有多大看头,在集合中主要有几个东西实现它,一个是ArrayList,一个是Linklist还有就是Vector。

** # 回到ArrayList **

现在我们来正式看看ArrayList的源码
ArrayList其实就是一个动态数组,与传统不同的就是,它可以动态扩容。
private static final int DEFAULT_CAPACITY = 10;这个是ArrayList默认的容量
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};默认构造方法为空对的数组
private static final Object[] EMPTY_ELEMENTDATA = {};初始化大小为0的数组
transient Object[] elementData; //这个元素的数据,当然底层实现还是数组,等会儿会知道怎么实现扩容了。
private int size;目前ArrrayList的大小
第一个构造方法:初始化了想要的数组容量
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[]
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
ensureCapacity方法是计算当前最小的容量大小;
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA
? 0
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);//转到ensureExplicitCapacity方法
}
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;//修改计数器+1,准备扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);//如果当前的容量小于当前所需容量,转到grow进行扩容
}
//真正的扩容方法
private void grow(int minCapacity) {
int oldCapacity = elementData.length;//先记录原来的容量大小
int newCapacity = oldCapacity + (oldCapacity >> 1);//相当于扩容2倍,在加上自己原来的容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);//ArrayList的数组扩容,Arrays.copyOf方法底层调用的是系统的System.copy方法,所以特别注意尽量避免进行扩容,以免消耗性能
}
public int size()
public boolean isEmpty()
public boolean contains(Object o)
public int indexOf(Object o)
public int lastIndexOf(Object o)
public Object clone()
public Object[] toArray()
public T[] toArray(T[] a)
E elementData(int index)
public E get(int index)
public E set(int index, E element)
public boolean add(E e) {//照道理应该添加操作要进行modCount+1
ensureCapacityInternal(size + 1); // modCount在看是否要扩容的时候+1了
elementData[size++] = e;
return true;
}
public void add(int index, E element)//在第index插入数据
public E remove(int index) {//删除元素也是一个重要的东西
rangeCheck(index);//检查index是否越界
modCount++;//修改次数+1
E oldValue = elementData(index);//oldValue
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){//这个就是按照对象移除,最终都找到fastremove
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) {//fastremove,由对象移除而来
modCount++;修改次数+1
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);*这里要特别注意特别注意的:如果要求删除一个数字里面某一个东西,比如删除“a”,“b”,"b"中的b,如果直接从前往后遍历删除,那么当遍历到第一个b时,删除第一个b,那么后面的元素往前移动,第二个b就移到了第一个b的位置,而下一次循环就到了最后一个位置,但是最后一个位置已经被置为空,且原本的第二个b也到了前面,就删除不了了,所以注意的就是一定要从后往前遍历,或者用其他方法。
elementData[–size] = null; // clear to let GC do its work由GC回收
}
protected void removeRange(int fromIndex, int toIndex)
public void clear()
public boolean addAll(Collection<? extends E> c)
public boolean addAll(int index, Collection<? extends E> c)
private void rangeCheck(int index)
private void rangeCheckForAdd(int index)
private String outOfBoundsMsg(int index)
public boolean removeAll(Collection<?> c)
public boolean retainAll(Collection<?> c) //保存包含指定的集合的元素,调用batchRemove实现
private boolean batchRemove(Collection<?> c, boolean complement)
private void writeObject(java.io.ObjectOutputStream s)
private void writeObject(java.io.ObjectOutputStream s)序列化过程
private void readObject(java.io.ObjectInputStream s)反序列化
public ListIterator listIterator(int index) {//剩下的就是一些迭代器了,就和其父类差不多。我就不一一列举了
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}


总结


上面列出了一些主要的类,方法,和变量,其中很重要的,有点坑对的我就做了详细的分析。其他的我就没有具体说了。
通过上面的源码我们知道,Arraylist是线程不安全的。 当一个线程修改,一个线程访问时候,它的modCount可能会发生不一致,导致错误。ArrayList是有索引顺序的,且里面的元素可以重复
而且ArrayList无非就是一个动态扩容的动态数组,其实原理和数组一模一样罢了。
优势就是能够随机索引,实现扩容,但是呢插入和删除的消耗还是挺大的,一般适合随时查询的数列吧。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小满锅lock

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值