您的位置:首页 > 其它

集合源码学习(三):ArrayList

2017-10-11 00:09 363 查看
Java集合里面最简单的估计就是
ArrayList
了,当然在我刚接触Java时,甚至接触相当一段时间,我都不知道
ArrayList
原理的。这两天把
ArrayList
源码仔细读了一遍,感觉如果把注释完的代码贴出来还不如直接看jdk文档。我把一些核心的地方找出来,下面将从以下几个方面来学习
ArrayList


什么是ArrayList?

其实ArrayList里面的数据结构知识就是线性表的应用。

关于modCount

modCount
是ArrayList继承自
AbstractArrayList
的一个字段。ArrayList有个fast-fail机制。fail-fast产生的原因就在于程序在对 Collection 进行迭代(Iterator操作)时,某个线程对该 Collection 在结构上对其做了修改(add和delete操作),这时迭代器就会抛出 ConcurrentModificationException 异常信息,从而产生 fail-fast。

jdk注释为:

The iterators returned by this class's iterator and listIterator methods are fail-fast:
if the vector is structurally modified at any time after the iterator is created, in
any way except through the iterator's own remove or add methods, the iterator will throw
a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator
fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at
an undetermined time in the future. The Enumerations returned by the elements method are not fail-fast.

Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking,
impossible to make any hard guarantees in the presence of unsynchronized concurrent modification.
Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would
be wrong to write a program that depended on this exception for its correctness: the fail-fast
behavior of iterators should be used only to detect bugs.


定义

public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable


从定义头的代码可以看出,ArrayList继承自AbstractList,支持泛型,可以随机存取,可以克隆,可以序列化都在代码中有所体现。

是否可为null?

可以

长度以及自动增长的长度

默认情况下,ArrayList内置数组长度为10,

/**
* 如果不指定arraylist大小,java8里面默认大小是10.
*/
private static final int DEFAULT_CAPACITY = 10;


如果范围不够了,自动增长长度将为原长度的1.5倍

/**
* 因为是按照老数组长度大小的1.5倍去调的,所以如果给定的minCapacity没有其1.5倍这么大,
* 还是会调到老数组长度1.5倍。
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);         //老数组大小的1.5倍。
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倍则取指定大小

ArrayList的最大长度

在源码中,有
MAX_ARRAY_SIZE
指定
ArrayList
最大长度为:

/**
* array的最大长度,减去8的原因是,有些vm的header words可能会占据一些空间。
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;


contains(Object o)的实现原理

实际上,在
ArrayList
中,需要判断
o
是否为null,如果为null,则是用
=
判断相等,而不为null时,则是使用equals判断。而整个判断的流程就是遍历所有元素,如果存在就返回true,否则返回false。

/**
*判断是否含有某个元素。
*/
public boolean contains(Object o) {
return indexOf(o) >= 0;
}

/**
* 判断的根据是遍历一遍,并且是通过equals遍历的。注意equals的用法与注意事项。
* 包括null。
*/
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;
}


clone方法

ArrayList
实现
clone
方法其实比较简单,这里将clone方法提出来是觉得一个细节处理的很好,就是
v.modCount=0
这句代码。如果没有这句代码,把本类的
modCount
也克隆过去可就不好了。

/**
* clone方法,注意此时还把v的modCount属性设为0了,防止由于本身是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);
}
}


set,get,add,remove等方法

set方法比较简单,随机访问数组,并且把该下标值设为相应值。

rangeCheck
为越界检查的方法。

/**
* 设置泛型的类型
*/
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}


get方法:

/**
* 得到泛型的类型
*/
public E get(int index) {
rangeCheck(index);
return elementData(index);
}


普通add:在末尾添加就可以了。

/**
* 增加泛型的类型,在末尾增加
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1);  // Increments modCount!!
elementData[size++] = e;
return true;
}


有指定位置的add和delete:这样的话,就需要调整一遍数组,如果在中间插入(add),就需要把其后面的往后挪一个位置。另外如果在中间删除,就需要把后面的都往前面挪一个位置。不要问为什么要挪,这是基础。

/**
* 将特定的元素插入到特定的位置。
* 把后面的元素往后统一挪一个。
*/
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++;
}


当然还有其他的一些方法例如

protected void removeRange(int fromIndex, int toIndex)


public boolean addAll(Collection<? extends E> c)


public boolean remove(Object o)






其他的增加,删除,修改等操作和上面讲的原理差不多,这里不赘述。

其他方法

另外在ArrayList还支持了对流操作:

/**
* 写入流
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
//这样的方法严格控制单个线程
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
/**
* 读入流里面的数据
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA;
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in capacity
s.readInt(); // ignored
if (size > 0) {
// be like clone(), allocate array based upon size not capacity
ensureCapacityInternal(size);
Object[] a = elementData;
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
a[i] = s.readObject();
}
}
}


既然是可以随机访问的集合,所以ArrayList里面还自己实现了可以返回特殊位置的ListIterator,通过构造方法传入index,则可以返回一个从该位置开始的迭代器(Iterator)

/**
* 返回一个特殊位置index的迭代器。
*/
public ListIterator<E> listIterator(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}


SubList

在ArrayList中,几乎花了一半的代码来讲SubList,而SubList是什么呢?其实SubList就是ArrayList,读名字读得出来,就是子串,里面的操作基本上是和ArrayList相同,原理也相同,而使用的数组则是直接或间接使用ArrayList里面的
elementData
。这里就不多说了。

Java8新增的Spliterator

这里可以看我另一篇博客,里面专门以ArrayListSpliterator分析过:

“集合源码学习(二):Spliterator”
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: