小标
2018-10-12
来源 :
阅读 1297
评论 0
摘要:本文主要向大家介绍了JAVA语言集合框架源码解析之ArrayList,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。
本文主要向大家介绍了JAVA语言集合框架源码解析之ArrayList,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。
ArrayList 可能是很多人使用得最为频繁的容器类了,ArrayList 实现了 List 接口,是一个有序容器,即存放元素的顺序与添加顺序相同,允许添加相同元素,包括 null ,底层通过数组来实现数据存储,容器内存储的元素个数不能超出数组空间。所以向容器中添加元素时如果发现数组空间不足,容器会自动对底层数组进行扩容操作
ArrayList 的类声明
public class ArrayList<e> extends AbstractList<e>
implements List<e>, RandomAccess, Cloneable, java.io.Serializable</e></e></e>
从其实现的几个接口可以看出来,ArrayList 是支持快速访问,可克隆,可序列化的
包含的成员变量
//序列化ID
private static final long serialVersionUID = 8683452581122892189L;
//集合默认的初始大小
private static final int DEFAULT_CAPACITY = 10;
//如果外部为集合设置的初始化大小为 0,则 elementData 指向空数组对象 EMPTY_ELEMENTDATA
private static final Object[] EMPTY_ELEMENTDATA = {};
//如果在初始化集合时使用的是无参数的构造函数,则 elementData 指向空数组对象 DEFAULTCAPACITY_EMPTY_ELEMENTDATA
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//包含实际元素的数组
transient Object[] elementData;
//集合大小
private int size;
elementData 是一个 Object 类型的数组对象,即可用来存放任何对象类型,也是 ArrarList 中用来存放数据的容器。而 ArrayList 是一个泛型类,我们在初始化时就直接指定了数据类型,Java泛型只是编译器为我们提供的语法糖,方便我们在向 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() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//传入一份初始数据,以此进行初始化
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 {
this.elementData = EMPTY_ELEMENTDATA;
}
}
可以在初始化 ArrayList 的时候传入集合的初始化大小,这通常来说都是更为高效率一些的,因为如果是让集合在赋值的过程中自动扩容,则可能会需要进行多次扩容操作,而每次扩容都需要复制原有数据到新数组,这会导致运行效率降低
看看我们常用的几个存取和移除数据的方法
在获取指定索引处的元素时,都是直接通过坐标指向该元素 (E) elementData[index],而无需从头开始遍历集合,所以说 ArrayList 的遍历效率较高
//通过索引直接访问数组
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
//获取索引 index 处的元素值
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
//将索引 index 出的元素值置为 element,并返回原始数值
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
ArrayList 在存入数据时相对来说就不是那么理想了
如果是直接向集合尾端添加数据 add(E e),则先检查是否需要扩容,需要的话则创建一个新的符合大小的数组,并将原数组中的元素移到新数组中,再向数组尾端添加待存入的元素
如果是向集合非尾端位置添加数据 add(int index, E element),一样需要先检查是否需要扩容,然后将数组中索引 index 后的所有元素向后推移一位,然后将 element 插入到空出的位置上
由此看出来,向集合添加元素由于可能会导致数组扩容,从而导致数组元素的大量移动,所以说 ArrayList 存入数据的效率并不高
//向集合添加数据
public boolean add(E e) {
//检查是否需要扩容
ensureCapacityInternal(size + 1);
//赋值
elementData[size++] = e;
return true;
}
//将元素 element 添加索引 index 位置
public void add(int index, E element) {
rangeCheckForAdd(index);
//检查是否需要扩容
ensureCapacityInternal(size + 1);
//将索引 index 后的所有数值向后推移一位
System.arraycopy(elementData, index, elementData, index + 1,size - index);
//将 element 插入到空出的位置
elementData[index] = element;
//集合大小加1
size++;
}
以上说的是存入单个元素,此外还有存入整个集合的情况
//向集合添加数据
//如果待添加的数据不为空则返回 true,否则返回 false
public boolean addAll(Collection<!--? extends E--> c) {
Object[] a = c.toArray();
int numNew = a.length;
//检查是否需要扩容
ensureCapacityInternal(size + numNew);
//将数组 a 复制到 elementData 的尾端
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
//从指定索引处添加数据
//如果待添加的数据不为空则返回 true,否则返回 false
public boolean addAll(int index, Collection<!--? extends E--> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
//检查是否需要扩容
ensureCapacityInternal(size + numNew);
//需要移动的数组元素数量
int numMoved = size - index;
//因为要添加的数据可能刚好是从数组最尾端开始添加,所以 numMoved 可能为 0
//所以只在 numMoved > 0 的时候才需要对数组的元素值进行移动,以此空出位置给数组 a
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew, numMoved);
//将数组 a 包含的数据添加到 elementData 中
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
再看下移除元素的方法
因为数组是一种内存地址连续的数据结构,所以移除某个元素同样可能导致大量元素的移动
//移除指定索引处的元素值,并返回该值
public E remove(int index) {
rangeCheck(index);
modCount++;
//待移除的元素值
E oldValue = elementData(index);
//因为要移除元素导致需要移动的元素数量
int numMoved = size - index - 1;
//因为要移除的元素可能刚好是数组最后一位,所以 numMoved 可能为 0
//所以只在 numMoved > 0 的时候才需要对数组的元素值进行移动
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index, numMoved);
//不管数组是否需要对元素值进行移动,数组的最后一位都是无效数据了
//此处将之置为 null 以帮助GC回收
elementData[--size] = null;
return oldValue;
}
//移除集合中包含的第一位元素值为 o 的对象
//如果包含该对象,则返回 true ,否则返回 false
<code class
本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注编程语言JAVA频道!
喜欢 | 1
不喜欢 | 0
您输入的评论内容中包含违禁敏感词
我知道了

请输入正确的手机号码
请输入正确的验证码
您今天的短信下发次数太多了,明天再试试吧!
我们会在第一时间安排职业规划师联系您!
您也可以联系我们的职业规划师咨询:
版权所有 职坐标-一站式AI+学习就业服务平台 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号