持续推送技术干货
目录
深入探讨源码之ArrayList
ArrayList
类图ArrayList
的数据结构ArrayList
的关键属性ArrayList
构造方法ArrayList
常用方法add方法ArrayList
中的fast-fail
机制add(i,o)方法set(i,o)方法get(i)方法remove(index)方法remove(Object)方法clear方法indexOf(o)
方法
深入探讨源码之ArrayList
java.util.ArrayList
位于JDK
的rt.jar中;出生于JDK1.2
。本文JDK
版本基于JDK1.8
。
List接口的可调整大小的数组实现,实现了所有可选的List操作,允许所有数据都为null。类Vector类似,ArrayList
是线程不安全的,Vector是线程安全的。
ArrayList
类图
上面这张图基本上描述的很清晰了,实现了四个接口一个抽象类。它继承了AbstractList
抽象类,实现了List、RandomAccess
, Cloneable
,Serializable
接口。
- 它继承于
AbstractList
,实现了List
,RandomAccess
随机访问,Cloneable
可克隆,java.io.Serializable
序列化]这些接口。 ArrayList
继承了AbstractList
,实现了List。它是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。ArrayList
实现了RandmoAccess
接口,即提供了随机访问功能。ArrayList
实现了Cloneable
接口,即覆盖了函数clone(),能被克隆。ArrayList
实现java.io.Serializable
接口,这意味着ArrayList
支持序列化,能通过序列化去传输。- 和Vector不同,
ArrayList
中的操作不是线程安全的。所以,建议在单线程中才使用ArrayList
,而在多线程中可以选择Vector或者CopyOnWriteArrayList
。
ArrayList
的数据结构
数据结构往往是它的灵魂所在,理解底层的数据结构其实就理解了该类的实现思路,具体的实现细节再具体分析。ArrayList
的数据结构是:
说明:底层的数据结构就是数组,数组元素类型为Object类型,即可以存放所有类型数据。我们对ArrayList
类的实例的所有的操作底层都是基于数组的。
ArrayList
的关键属性
//初始化大小为10
private static final int DEFAULT_CAPACITY = 10;
//空对象数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//缺省空对象数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//存数据的数组
transient Object[] elementData;
//当前ArrayList的大小
private int size;
//ArrayList最大容量
//Integer.MAX_VALU=(2的31次方)-1
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
ArrayList
构造方法
//开发人员使用最多的构造方法,全部使用默认参数
public ArrayList() {
this.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);
}
}
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
常用方法
add方法
往ArrayList
中添加元素,使用demo
public class ArrayListDemo {
public static void main(String[] args) {
ArrayList<String> arrayList=new ArrayList<>();
arrayList.add("Java后端技术栈");
arrayList.add("tian");
arrayList.add("Java");
arrayList.add("DB");
Iterator<String> mIterator = arrayList.iterator();
while (mIterator.hasNext()){
if ("tian".equals(mIterator.next())){
arrayList.add("C");
}
}
}
}
add方法源码分析
public boolean add(E e) {
//确保内部容量,size为elementData的长度,本次添加一个
//即size+1
//使用第一个构造方法,第一次添加数据的时候,size=0
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
//elementData是空数组,minCapacity=0+1=1
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//两个数组都是空的,所以这里是相等的
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//返回DEFAULT_CAPACITY=10和minCapacity=1比较,谁大返回谁
//那么这里就返回10
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
//用来标记当前arraylist集合操作变化的次数
modCount++;
//如果minCapacity>elementData.length
//minCapacity=10,elementData.length=0
if (minCapacity - elementData.length > 0){
grow(minCapacity);
}
}
private void grow(int minCapacity) {
//将扩充前的elementData大小给oldCapacity
int oldCapacity = elementData.length;
//newCapacity就是1.5倍的oldCapacity
//第一次添加元素的时候,newCapacity依旧是0
int newCapacity = oldCapacity + (oldCapacity >> 1);
//这句话就是适应于elementData就空数组的时候,length=0,
//那么oldCapacity=0,newCapacity=0,所以这个判断成立,
//在这里就是真正的初始化elementData的大小了,
//就是为10.前面的工作都是准备工作。
if (newCapacity - minCapacity < 0){
newCapacity = minCapacity;
}
//如果newCapacity超过了最大的容量限制,就调用hugeCapacity,
//也就是将能给的最大值给newCapacity
//比较一下newCapacity与(2的31次方)-1谁大
if (newCapacity - MAX_ARRAY_SIZE > 0){
newCapacity = hugeCapacity(minCapacity);
}
// minCapacity is usually close to size, so this is a win:
//新的容量大小已经确定好了,就copy数组,改变容量大小。
elementData = Arrays.copyOf(elementData, newCapacity);
}
//OOM或者就是赋值最大容量
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
//如果minCapacity都大于MAX_ARRAY_SIZE,那么就Integer.MAX_VALUE返回,
//反之将MAX_ARRAY_SIZE返回。因为maxCapacity是三倍的minCapacity,
//可能扩充的太大了,就用minCapacity来判断了。
//Integer.MAX_VALUE:2147483647 MAX_ARRAY_SIZE:2147483639
//也就是说最大也就能给到第一个数值。还是超过了这个限制,
//就要溢出了。相当于arraylist给了两层防护。
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}
ArrayList
中的fast-fail
机制
运行上面demo
还记得上面源码中ensureExplicitCapacity
方法中有个modCount
变量么?
抛出的异常正是我们刚才提到的ConcurrentModificationException
并发修改异常,正是由于fail-fast机制导致的。在什么情况下会出现该异常呢?我先简单说下,一般情况下有两种情况,一种情况发生在多线程操作,当a线程正在通过迭代器操作集合arrayList
时,同时b线程对arrayList
进行添加或者删除元素,会触发fail-fast机制,抛出该异常。另一种情况是在迭代集合arrayList
的过程中对arrayList
集合进行元素添加或者删除操作时,会触发fail-fast机制,抛出该异常。归根结底就是当我们进行集合数据迭代时,有其他操作修改了当前Arraylist
的modCount
的值,导致modCount != expectedModCount
,会抛出该异常。
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
// next 元素角标
int cursor; // index of next element to return
// last 元素角标
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
public E next() {
//检查共变性
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
final void checkForComodification() {
//当modCount != expectedModCount时,
//会抛出ConcurrentModificationException异常
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
add、remove、clear元素都会使得modCount
变化。
add(i,o)方法
添加到指定的位置。
使用demo
public class ArrayListDemo1 {
public static void main(String[] args) {
ArrayList<String> arrayList=new ArrayList<>();
arrayList.add(1,"Java后端技术栈");
}
}
具体源码
//index为即将要存放数据的位置,element为数据
public void add(int index, E element) {
//添加之前,先检查index是否满足条件
rangeCheckForAdd(index);
//前面已经分析了
ensureCapacityInternal(size + 1); // Increments modCount!!
//将index及其后边的所有的元素整块后移,空出index位置
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//将元素存放在index位置上
elementData[index] = element;
//数组大小加1
size++;
}
private void rangeCheckForAdd(int index) {
//如果使用第一种构造方法,那么size==0
//如果index>size或者index<0就会抛数组越界异常
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
运行demo,这个方法慎用,平时工作使用的不是很多,因为在使用的时候,需要保证不满足
index > size和Index<0中的任何一个条件,否则将出现:
set(i,o)方法
使用demo
public class ArrayListDemo1 {
public static void main(String[] args) {
ArrayList<String> arrayList=new ArrayList<>();
arrayList.add("Java后端技术栈");
arrayList.add("tian");
arrayList.add("Java");
arrayList.set(1,"C");
for (String string:arrayList){
System.out.println(string);
}
}
}
源码
//把Index位置上的旧值拿出来,然后把新值放进去
public E set(int index, E element) {
rangeCheck(index);
//获取index位置上的旧值
E oldValue = elementData(index);
//把index位置上设置新值
elementData[index] = element;
//返回旧值
return oldValue;
}
//index大于ArrayList的大小,就明显是数组越界了。
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
get(i)方法
获取数据
//检查index是否越界,然后获取index上的数据
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index];
}
remove(index)方法
public E remove(int index) {
//范围检查
rangeCheck(index);
//modCount加1
modCount++;
//取出旧值
E oldValue = elementData(index);
//获取从哪个下标开始移动数据
int numMoved = size - index - 1;
//如果删除的不是最后一个元素
if (numMoved > 0)
//删除的元素到最后的元素整块前移
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//将最后一个元素设为null,方便垃圾回收集在下次gc的时候就会回收掉了
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
remove(Object)方法
和remove(index)雷士,只是多了一层遍历而已。性能明细没有remove(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(index) 类似了
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
}
clear方法
public void clear() {
modCount++;
// clear to let GC do its work
//清楚就是把ArrayList中的所有对象置为null,方便垃圾收集器收集
for (int i = 0; i < size; i++)
elementData[i] = null;
//最后把size置为0
size = 0;
}
indexOf(o)
方法
获取o所在的下标,如果ArrayList中没有o,那么就返回 -1;
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;
}
标签:index,minCapacity,--,ArrayList,elementData,int,源码,size
From: https://blog.51cto.com/u_11702014/6235392