您的位置:首页 > 编程语言 > Java开发

使用ArrayList时代码内部发生了什么(jdk1.7)?

2018-03-30 12:40 387 查看

前言

ArrayList(这里的ArrayList是基于jdk1.7)是在项目中经常使用的集合类,例如我们从数据库中查询出一组数据。这篇文章不去剖析它的继承和实现,只是让我们知道实例化及增删改查时它的内部代码是怎么实现的。

public class TestList {
@Test
public void testArrayList(){
List<Integer> list = new ArrayList<>();

for (int i = 0; i < 12; i++) {
list.add(i);
}
}
}


  

实例化

List<Integer> list = new ArrayList<>();


  

我们先来看看上面这段实例化ArrayList时,内部发生了什么。

这调用的是ArrayList的无参构造函数,如下



public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}


  

步骤:



调用父类的构造函数(AbstractList的构造函数)

赋值elementData(一个空数组{})



这里把ArrayList内部的属性说明下,一共有DEFAULT_CAPACITY、EMPTY_ELEMENTDATA、elementData、size。其中DEFAULT_CAPACITY和EMPTY_ELEMENTDATA是被定义为static和final的。而elementData是我们添加元素时存储的数组,size就是这个数组的大小,DEFAULT_CAPACITY就是数组默认的大小,EMPTY_ELEMENTDATA是一个空数组。

 /**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;

/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};

/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to
* DEFAULT_CAPACITY when the first element is added.
*/
private transient Object[] elementData;

/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;


  

实例化部分就到这里了,其他有参的这里就不做介绍,需要的可以自己去看看,接下来看看添加元素

添加元素

for (int i = 0; i < 12; i++) {
list.add(i);
}


  

这里循环添加了12个元素,是为了查看ArrayList的第一次扩容,接下来看看ArrayList里面是怎么实现的

add方法

 public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}


  

add方法中的步骤如下



调用ensureCapacityInternal,传入参数(size+1)

elementData数组对象赋值

返回true



ensureCapacityInternal方法

 private void ensureCapacityInternal(int minCapacity) {
if (elementData == EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}

ensureExplicitCapacity(minCapacity);
}


  

这个方法中传入的参数名称为minCapacity,翻译为中文就是最小容量。

这个方法首先判断当前数组对象elementData是不是等于空对象EMPTY_ELEMENTDATA。大家可以看下实例化时elementData对象的赋值(如下)。

 public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}


  

如果是,则最小容量minCapacity重新赋值为DEFAULT_CAPACITY和minCapacity中最大的一个数。

然后调用ensureExplicitCapacity方法。断点内容如下

minCapacity被赋值为DEFAULT_CAPACITY



接下来是ensureExplicitCapacity方法

ensureExplicitCapacity方法

 private void ensureExplicitCapacity(int minCapacity) {
modCount++;

// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}


  

modCount是ArrayList的父类AbstractList的一个属性,记录被修改的次数,也是通过这个触发fail-fast机制的,这里不过多说明。

下面就是比较传入的最小容量minCapacity减去elementData数组的长度是否大于0(也就是最小容量minCapacity是否大于elementData数组的长度),是的话调用grow方法。

grow方法

这个方法就是ArrayList的扩容方法,接下来看看方法内部代码

 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);
}


  

由于是第一次调用add方法,所以minCapacity值为ArrayList的默认容量10,如下



接下来定义一个旧容量oldCapacity,值为elementData数组长度

然后定义一个新容量newCapacity,值为旧容量oldCapacity加上(旧容量oldCapacity右移1位,也就是除以2,不四舍五入)

接着比较新容量newCapacity和传入的最小容量minCapacity,谁大就赋值给新容量newCapacity

如果新容量newCapacity比定义的数组最大容量MAX_ARRAY_SIZE还大的话,就调用hugeCapacity方法,看是否抛出异常还是赋最大值

调用Arrays.copyOf进行数组扩容

获取元素

获取元素有三种方式

迭代器Iterator遍历

通过索引

foreach循环获取

这里就介绍下通过迭代器Iterator遍历吧。

 public Iterator<E> iterator() {
return new Itr();
}


  

Itr是ArrayList一个实现Iterator的内部类,通过这个对象来获取ArrayList中存储的元素。代码如下

 /**
* An optimized version of AbstractList.Itr
*/
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;

public boolean hasNext() {
return cursor != size;
}

@SuppressWarnings("unchecked")
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];
}

public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();

try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}

final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}


  

总结

当我们实例化ArrayList时(调用无参构造函数),这个对象里面存储元素的数组还只是一个空数组。

在第一次调用add方法时(也就是第一次添加元素时),对存储元素的数组elementData进行第一次扩容,扩容的数组长度为ArrayList内部定义的默认容量10,。

当插入第11个数组元素时,进行第二次扩容,扩容的长度为原先的容量加上(原先容量/2),即原先容量的1.5倍。扩容的长度规则是10>>15>>22>>33...
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐