java arraylist min_Java ArrayList 实现实例讲解

ArrayList概述:

ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存。

ArrayList不是线程安全的,只能用在单线程环境下,多线程环境下可以考虑用Collections.synchronizedList(List l)函数返回一个线程安全的ArrayList类,也可以使用concurrent并发包下的CopyOnWriteArrayList类。

ArrayList实现了Serializable接口,因此它支持序列化,能够通过序列化传输,实现了RandomAccess接口,支持快速随机访问,实际上就是通过下标序号进行快速访问,实现了Cloneable接口,能被克隆。

每个ArrayList实例都有一个容量,该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向ArrayList中不断添加元素,其容量也自动增长。自动增长会带来数据向新数组的重新拷贝,因此,如果可预知数据量的多少,可在构造ArrayList时指定其容量。在添加大量元素前,应用程序也可以使用ensureCapacity操作来增加ArrayList实例的容量,这可以减少递增式再分配的数量。

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

下面对java arraylist做一个记录和总结吧

public class arraylist {

/**

* 存放集合的元素

*

*/

private transient Object[] elementData;

/** 元素的大小 */

private int size;

定义了一个泛型类,一个object的数组和一个私有变量来记录该集合的元素数量,原文多了一个私有变量,我也不知道干嘛用的,作者也没解释也没提及到,我没使用也没事

/**

* 根据指定大小初始化

* @param initialCapacity

*/

public arraylist(int initialCapacity){

super();

if(initialCapacity<=0){

//抛异常

throw new IllegalArgumentException("初始化参数不能小于0");

}else{

//初始化数组

this.elementData=new Object[initialCapacity];

}

}

/**

* 默认初始化

*/

public arraylist(){

this(10);

}

/**

* 根据一个集合类初始化

* @param c 一个必须继承了Collection接口的类

*/

public arraylist(Collection extends E> c){

//初始化

elementData=c.toArray();

size=elementData.length;

//如果不是任意类型的数组就转换Objec类型

if (elementData.getClass() != Object[].class){

elementData=Arrays.copyOf(elementData,size, Object[].class);

}

}

3个初始化方法,根据默认大小进行数组的初始化,给定大小初始化和传递一个继承了Collection集合接口的类进行转换赋值初始化

/**

* 扩容集合

* @param minCapacity

*/

public void ensureCapacity(int minCapacity){

/** 当前数组的大小 */

int oldCapacity = elementData.length;

if (minCapacity > oldCapacity) {

/**

* oldData 虽然没有被使用,但是这是关于内存管理的原因和Arrays.copyOf()方法不是线程安全

* oldData在if的生命周期内引用elementData这个变量,所以不会被GC回收掉

* 当Arrays.copyOf()方法在把elementData复制到newCapacity时,就可以防止新的内存或是其他线程分配内存是elementData内存被侵占修改

* 当结束是离开if,oldData周期就结束被回收

*/

Object oldData[] = elementData;

int newCapacity = (oldCapacity * 3)/2 + 1; //增加50%+1

if (newCapacity < minCapacity)

newCapacity = minCapacity;

//使用Arrays.copyOf把集合的元素复制并生成一个新的数组

elementData = Arrays.copyOf(elementData, newCapacity);

}

}

这是一个核心的方法,集合的扩容,其实是对数组的扩容,minCapacity集合的大小,进行对比判断是否应该进行扩容,使用了Arrays.copyOf()方法进行扩容,

原文有进行详细的解释,这个方法把第一个参数的内容复制到一个新的数组中,数组的大小是第二个参数,并返回一个新的数组,关于oldData的变量上文有详细的注释

/**

* 检查索引是否出界

* @param index

*/

private void RangeCheck(int index){

if(index > size || index < 0){

throw new IndexOutOfBoundsException("下标超出,Index: " + index + ", Size: " +size);

}

}

一个下标的检索是否出 1 /**

* 添加元素

* 将指定的元素添加到集合的末尾

* @param e 添加的元素

* @return

*/

public boolean add(E e){

ensureCapacity(size+1);

elementData[size]=e;

size++;

return true;

}

添加元素,先进行扩容,在赋值,然后元素加一,注意 size+1 字段size并没有加一,这里进行的是算术的运算,所以在后面才需要进行自增

/**

* 添加元素

* 将元素添加到指定的位置

* @param index 指定的索引下标

* @param element 元素

* @return

*/

public boolean add(int index, E element){

RangeCheck(index);

ensureCapacity(size+1);

// 将 elementData中从Index位置开始、长度为size-index的元素,

// 拷贝到从下标为index+1位置开始的新的elementData数组中。

// 即将当前位于该位置的元素以及所有后续元素右移一个位置。

System.arraycopy(elementData, index, elementData, index+1, size-index);

elementData[index]=element;

size++;//元素加一

return true;

}

这里不同的是 System.arraycopy(elementData, index, elementData, index+1, size-index);

这是一个c的内部方法,详细的原文有解释,这里就不说了,这个也是整个ArrayList的核心所在,也Arrays.copyOf()的内部实现原理

/**

* 添加全部元素

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

* @param c

* @return

*/

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

Object[] newElement=c.toArray();

int elementLength=newElement.length;

ensureCapacity(size+elementLength);

//从newElement 0的下标开始,elementLength个元素,elementData size的下标

System.arraycopy(newElement, 0, elementData, size, elementLength);

size+=elementLength;

return elementLength!=0;

}

基本上其他方法都只是根据不同的情况进行不同的处理,比如通过接口把数据对象传递进来然后获取长度进行扩容,在把数据使用System,arraycopy复制到新的数组中

/**

* 指定位置,添加全部元素

* @param index 插入位置的下标

* @param c 插入的元素集合

* @return

*/

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

if(index > size || index < 0){

throw new IndexOutOfBoundsException("Index: " + index + ", Size: " +size);

}

Object[] newElement=c.toArray();

int elementLength=newElement.length;

ensureCapacity(size+elementLength);

int numMoved=size-index;

//判断插入的位置是否在数组中间

if(numMoved>0){

//把index插入位置的后面的所有元素往后移

//elementData index下标开始的numMoved个元素插入到elementData 的index+elementLength位置

System.arraycopy(elementData, index, elementData, index+elementLength, numMoved);

}

//把newElement里从0开始的elementLength个元素添加到elementData index开始的位置

System.arraycopy(newElement, 0, elementData, index, elementLength);

size += elementLength;

return elementLength != 0;

}

/**

* 指定下标赋值

* @param index

* @param element

* @return

*/

public E set(int index,E element){

RangeCheck(index);

E oldElement=(E)elementData[index];

elementData[index]=element;

return oldElement;

}

/**

* 根据下标取值

* @param index

* @return

*/

public E get(int index){

RangeCheck(index);

return (E)elementData[index];

}

/**

* 根据下标移除元素

* @param index

*/

public E remove(int index){

RangeCheck(index);

E oldElement=(E)elementData[index];

/** 移除的下标后面的元素数量 */

int numMoved=size-index-1;

//如果在数组范围内就进行移动

if(numMoved>0)

System.arraycopy(elementData, index+1, elementData, index, numMoved);

//移除

elementData[--size]=null;

return oldElement;

}

/**

* 根据元素移除

* @param obj

* @return

*/

public boolean remove(Object obj){

//Arraylist允许存放null,所以也要进行判断处理

if(obj==null){

for(int index=0;index

if(elementData[index]==null){

remove(index);

return true;

}

}

}else{

for(int index=0;index

if(obj.equals(elementData[index])){

remove(index);

return true;

}

}

}

return false;

}

/**

* 根据下标移除指定范围内的元素

* @param fromIndex 开始

* @param toIndex 结束

*/

protected void removeRange(int fromIndex, int toIndex){

RangeCheck(fromIndex);

RangeCheck(toIndex);

//要移动的元素数

int numMoved = size - toIndex;

//把toIndex后面的元素移动到fromIndex

System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved);

//要移除的元素数量

int newSize=size-(toIndex-fromIndex);

while(size!=newSize){

elementData[--size]=null;

}

}

/**

* 把数组容量调整到实际的容量

*/

public void trimToSize(){

int leng=elementData.length;

if(size

Object[] old=elementData;

elementData=Arrays.copyOf(elementData, size);

}

}

/**

* 把集合元素转换成数组

* @return

*/

public Object[] toArray(){

return Arrays.copyOf(elementData, size);

}

public T[] toArray(T[] a){

if(a.length

return (T[]) Arrays.copyOf(elementData,size, a.getClass());

}

//把集合元素复制到a数组中

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

if (a.length > size){

for(int index=size;index

a[index] = null;

}

}

return a;

}

基本上都是对数组进行操作和使用c的方法进行赋值移动等,详细的可以查看原文,原文中除了那个私有变量外也没多少问题,代码可以完美运行,这李要注意的和难点就会是System,arraycopy和Arrayist.copy()这2个方法

和在扩容方法里oldData这个变量的使用,这个变量真的很好,一开始我也不知道为什么要这么使用,在原文的末尾会进行解释。

以上所述是小编给大家介绍的Java ArrayList 实现实例讲解,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的,在此也非常感谢大家对脚本之家网站的支持!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值