java arraylist 实现原理_java集合之ArrayList的实现原理

1. ArrayList概述:

ArrayList是List接口的可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。

每个ArrayList实例都有一个容量,该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向ArrayList中不断添加元

素,其容量也自动增长。自动增长会带来数据向新数组的重新拷贝,因此,如果可预知数据量的多少,可在构造ArrayList时指定其容量。在添加大量元素

前,应用程序也可以使用ensureCapacity操作来增加ArrayList实例的容量,这可以减少递增式再分配的数量。

注意,此实现不是同步的。如果多个线程同时访问一个ArrayList实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步。

2. ArrayList的实现:

对于ArrayList而言,它实现List接口、底层使用数组保存所有元素。其操作基本上是对数组的操作。下面我们来分析ArrayList的源代码:

底层使用数组实现

private transient Object[] elementData;

构造方法

可以构造一个默认初始容量为0的空列表、构造一个指定初始容量的空列表以及构造一个包含指定collection的元素的列表,这些元素按照该collection的迭代器返回它们的顺序排列的。

public ArrayList(intinitialCapacity) {super();if (initialCapacity < 0)throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);this.elementData = newObject[initialCapacity];

}

/*** Constructs an empty list with an initial capacity of ten.*/

publicArrayList() {super();this.elementData =EMPTY_ELEMENTDATA;

}

/*** Shared empty array instance used for empty instances.

空数组*/

private static final Object[] EMPTY_ELEMENTDATA = {};

/*** Constructs an empty list with an initial capacity of ten.*/

publicArrayList() {super();this.elementData =EMPTY_ELEMENTDATA;

}

增加

//将指定的元素添加到元素的尾部

public booleanadd(E e) {

//扩容

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

elementData[size++] =e;return true;

}

//将指定的元素插入此列表中指定的位置

public void add(intindex, E element) {

//如果(index >size || index < 0)抛出IndexOutOfBoundsExceptionrangeCheckForAdd(index);//数组长度不足,进行扩容

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

//将elementData数组中从index位置开始,长度为size - index 的元素拷贝到,从下面index+1开始新的elementData中    //即将当前位于当前位置的元素及后面的元素向右移动一个位置

System.arraycopy(elementData, index, elementData, index + 1,

size-index);

elementData[index]=element;

size++;

}

// 按照指定collection的迭代器所返回的元素顺序,将该collection中的所有元素添加到此列表的尾部。

public boolean addAll(Collection extends E>c) {

Object[] a=c.toArray();int numNew =a.length;

ensureCapacityInternal(size+ numNew); //Increments modCount

System.arraycopy(a, 0, elementData, size, numNew);

size+=numNew;return numNew != 0;

}

// 从指定的位置开始,将指定collection中的所有元素插入到此列表中。

public boolean addAll(int index, Collection extends E>c) {

rangeCheckForAdd(index);

Object[] a=c.toArray();int numNew =a.length;

ensureCapacityInternal(size+ numNew); //Increments modCount

int numMoved = size -index;if (numMoved > 0)

System.arraycopy(elementData, index, elementData, index+numNew,

numMoved);

System.arraycopy(a,0, elementData, index, numNew);

size+=numNew;return numNew != 0;

}

查看

//返回列表中指定位置的元素

public E get(intindex) {

rangeCheck(index);returnelementData(index);

}

删除

//移除指定位置上的元素,返回指定位置上原来的元素

public E remove(intindex) {

//数组越界检测

rangeCheck(index);

modCount++;

E oldValue=elementData(index);

//移除指定位置上的元素,底层数组copy,数组元素向左移动一个位置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

returnoldValue;

}

//移除第一次出现的指定的元素(ArrayList中允许存放重复元素)

public booleanremove(Object o) {

// 由于ArrayList中允许存放null,因此下面通过两种情况来分别处理。if (o == null) {for (int index = 0; index < size; index++)if (elementData[index] == null) {

// 类似remove(int index),移除列表中指定位置上的元素。

fastRemove(index);return true;

}

}else{for (int index = 0; index < size; index++)if(o.equals(elementData[index])) {

fastRemove(index);return true;

}

}return false;

}

修改:

//用指定元素修改此列表中指定位置上得元素,并返回以前位于该位置上的元素

1 public E set(intindex, E element) {2 rangeCheck(index);3

4 E oldValue =elementData(index);5 elementData[index] =element;6 returnoldValue;7 }

数组的扩容

从上面介绍的向ArrayList中存储元素的代码中,我们看到,每当向数组中添加元素时,都要去检查添加后元素的个数是否会超出当前数组的长度,如果超 出,数组将会进行扩容,以满足添加数据的需求。数组扩容通过一个公开的方法ensureCapacity(int minCapacity)来实现。在实际添加大量元素前,我也可以使用ensureCapacity来手动增加ArrayList实例的容量,以减少递增式再分配的数量。

private void ensureCapacityInternal(intminCapacity) {if (elementData ==EMPTY_ELEMENTDATA) {

minCapacity=Math.max(DEFAULT_CAPACITY, minCapacity);

}

ensureExplicitCapacity(minCapacity);

}private void ensureExplicitCapacity(intminCapacity) {

modCount++;//overflow-conscious code

if (minCapacity - elementData.length > 0)

//扩容方法

grow(minCapacity);

}/*** The maximum size of array to allocate.

* Some VMs reserve some header words in an array.

* Attempts to allocate larger arrays may result in

* OutOfMemoryError: Requested array size exceeds VM limit*/

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;/*** Increases the capacity to ensure that it can hold at least the

* number of elements specified by the minimum capacity argument.

*

*@paramminCapacity the desired minimum capacity

扩容的主要方法*/

private void grow(intminCapacity) {//overflow-conscious code

int oldCapacity =elementData.length;

//新的数组容量约等于原有的数组容量的1.5倍,oldCapacity >> 1右移一位,newCapacity = oldCapacity + oldCapacity/2int 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);

}

//提供的公有的扩展容量的方法,可以手动增加ArrayList容量,可以减少递增式的再分配的数量

public void ensureCapacity(intminCapacity) {int minExpand = (elementData !=EMPTY_ELEMENTDATA)//any size if real element table

? 0

//larger than default for empty table. It's already supposed to be//at default size.

: DEFAULT_CAPACITY;if (minCapacity >minExpand) {

ensureExplicitCapacity(minCapacity);

}

}

从上述代码中可以看出,数组进行扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量的增长大约是其原容量的1.5倍。这种操作的代价是很 高的,因此在实际使用时,我们应该尽量避免数组容量的扩张。当我们可预知要保存的元素的多少时,要在构造ArrayList实例时,就指定其容量,以避免 数组扩容的发生。或者根据实际需求,通过调用ensureCapacity方法来手动增加ArrayList实例的容量。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值