java 容器(collection)--ArrayList 常用方法分析 源码分析

ArrayList 介绍

打开jdk源码看看官方文档的介绍

java 容器(collection)--ArrayList 常用方法分析 源码分析

粗糙的翻译下大致意思是:

List接口的可调整大小的数组实现。实现了所有可选的列表操作,并允许所有元素,包括 null 。除了实现List接口之外,这个类提供了操作数组大小的方法。

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; // non-private to simplify nested class access /**
* 元素个数
*/
private int size;

无参构造器

  /**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

这个相当于创建一个空数组:

this.elementData={};

有参构造器

/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
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);
}
}

他的健壮性比较好,我们按正常思维,只看第一个 initialCapacity > 0 的 if 分支:

相当于创建一个长度为 20 的数值:

this.elementData=new Object[20];

添加方法 add(Object obj)

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

这个代码技术含量挺高,不愧是老司机写的

ensureCapacityInternal(size + 1); 用来 检测空间容量是否够用(看下一个方法)

elementData[size++] = e; 就是添加元素相当于:

elementData[size]=e; size++;

检测空间容量是否够用

 private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

这个方法首先会在内部调用calculateCapacity(elementData, minCapacity) 计算容量:

private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}

当我们在新建一个ArrayList 对象上添加元素时:

Math.max(DEFAULT_CAPACITY, minCapacity); 相当于:

Math.max(10,1);

到这里容量计算完了,然后回去执行:

 private void ensureExplicitCapacity(int minCapacity) {
modCount++; // overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}

由于是第一次添加,if 条件相当于:

if( 10 - 0 > 0)

结果为true,会执行if语句块

grow(minCapacity); 执行的就是扩容方法。

扩容方法

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

我们是第一次添加,以上代码实际执行的就是:

int oldCapactiy=0;

int newCapacity=0+0>>1 结果为0

如果 0-10<0 为 true

newCapacity=10;

数组拷贝 elementData的长度就是 10

最后回到:

public boolean add(E e) {

ensureCapacityInternal(size + 1); // Increments modCount!!

elementData[size++] = e;

return true;

}

结果就是:

elementData[0] = e;

size++;

接下来看下add 的重载方法

add(int index, E element)

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

rangeCheckForAdd(index);是下标越界检测,我们先不看,下一行代码:

ensureCapacityInternal(size + 1);跟之前的默认位置天机一样先检测数组的空间容量。

关键是再往下一行代码;

执行的结果是把要插入位置开始到结束的所有元素往后挪一个位置,然后把空出的位置赋值为传入有的参数,同时把元素个数+1;

现在ArrayList对象的容量是10了,当我么一直做添加操作,容量满了会怎么样呢?

其实不管我们怎么添加,都会执行ensureCapacityInternal(size + 1) 来检测容量,而ensureCapacityInternal(size + 1)又会调用calculateCapacity(elementData, minCapacity)直到

ensureExplicitCapacity(int minCapacity)方法中的if语句成立时,执行

grow(minCapacity);进行扩容:

比如我们加到第11个元素时:

grow(minCapacity)方法中的

oldCapacity + (oldCapacity >> 1);返回15;

那么ArrayList的容量就增加到15了;

继续增加还是一样的操作。

至此,我们已经对ArrayList 创建,添加,扩容有了一定了解。

那么剩下的一些方法就是数组的操作了,很好理解。

ArrayList的其他方法

get(int index)根据索引获取元素对象

public E get(int index) {
rangeCheck(index);
//调用了elementData()的方法
return elementData(index);
}
@SuppressWarnings("unchecked")
E elementData(int index) {
//根据索引取出数组中索引对象
return (E) elementData[index];
}

size()

  public int size() {
// 返回集合记录的元素个数
return size;
}

isEmpty()

public boolean isEmpty() {
// 这个太简单了,不必多说
return size == 0;
}

set(int index, Object obj)

public E set(int index, E element) {
rangeCheck(index); E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}

就是数组操作,返回替换掉的元素

remove(int index)

 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; // 将最后一个位置设置为null return oldValue;
}

类似于前面的指定位置添加,返回原始元素对象

clear()

public void clear() {
modCount++; // clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null; size = 0;
}

循环遍历,所有 索引位置的元素赋值null,并把元素个数设置为0;

水平有限,先写到这了。

上一篇:【转】全Javascript的Web开发架构:MEAN和Yeoman【译】


下一篇:Struts2请求处理流程及源码分析