Java集合之ArrayList源码分析
1.简介
List在数据结构中表现为是线性表的方式,其元素以线性方式存储,集合中允许存放重复的对象,List接口主要的实现类有ArrayList和LinkedList。Java中分别提供了这两种结构的实现,这一篇文章是要熟悉下ArrayList的源码实现。ArrayList其实就是一组长度可变的数组,当实例化了一个ArrayList,该数据也被实例化了,当向集合中添加对象时,数组的大小也随着改变,这样它所带来的有优点是快速的随机访问,即使访问每个元素所带来的性能问题也是很小的,但缺点就是想其中添加或删除对象速度慢,当你创建的数组是不确定其容量,所以当我们改变这个数组时就必须在内存中做很多的处理,如你想要数组中任意两个元素中间添加对象,那么在内存中数组要移动所有后面的对象。使用示例如下:
package com.test.collections;
import java.util.ArrayList;
public class ArrayListTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
ArrayList<String> arrayList = new ArrayList<String>();
arrayList.add("A");
arrayList.add("B");
arrayList.add("C");
arrayList.add("D");
arrayList.add("E");
System.out.println(arrayList.add("F"));
System.out.println(arrayList.contains("A"));
System.out.println(arrayList.indexOf("E"));
System.out.println(arrayList.contains("A"));
System.out.println(arrayList.clone());
System.out.println(arrayList.lastIndexOf("D"));
System.out.println(arrayList.remove(1));
System.out.println(arrayList.remove("A"));
arrayList.trimToSize();
arrayList.add(1, "N");
arrayList.clear();
}
}
2.继承结构
ArrayList继承了AbstractList<E>,实现了List<E>,RandomAccess, Cloneable, Serializable这几个接口,在ArrayList的内部还有私有化的内部类Itr继承实现了Iterator<E>接口,ListItr继承了ArrayList<E>.Itr 并且实现了 ListIterator<E>接口。可以使用迭代器的相关方法。
3.源码分析
a:相关属性
通过源码可以发现ArrayList实现类里面含有的属性有private static final int DEFAULT_CAPACITY = 10;private static final Object[] EMPTY_ELEMENTDATA = new Object[0];private transient Object[] elementData;private int size;private static final int MAX_ARRAY_SIZE = 2147483639;
DEFAULT_CAPACITY :缺省默认的初始化数组大小
EMPTY_ELEMENTDATA :创建一个空的初始化数组
elementData:瞬时数组记录存放数组列表的元素(transient 关键字标识该属性能和对象一起进行串行序列化)
size :标识记录List的大小
MAX_ARRAY_SIZE :数组允许的最大元素数量
b:构造方法
public ArrayList(int paramInt) {
if (paramInt < 0)
throw new IllegalArgumentException("Illegal Capacity: " + paramInt);
this.elementData = new Object[paramInt];
}
public ArrayList() {
this.elementData = EMPTY_ELEMENTDATA;
}
public ArrayList(Collection<? extends E> paramCollection)
{
this.elementData = paramCollection.toArray();
this.size = this.elementData.length;
if (this.elementData.getClass() == [Ljava.lang.Object.class)
return;
this.elementData = Arrays.copyOf(this.elementData, this.size, [Ljava.lang.Object.class);
}
通过源码我们可以发现AarrayList提供了三种类型的构造函数:无参的构造函数ArrayList()是创建了一个空的ArrayList;带有一个整形参数的构造函数ArrayList(int) 等于了初始化了一个固定容量大小的ArrayList;提供了含有稽核参数的构造函数ArrayList(Collection)是通过将将该集合中的数据通过复制到新的ArrayList中并且将它赋值给elementData;
c:trimeToSize()
public void trimToSize() {
this.modCount += 1;
if (this.size >= this.elementData.length)
return;
this.elementData = Arrays.copyOf(this.elementData, this.size);
}
通过源码可以发现整个方法的作用就是去除了空的未被利用的null空间。作用只是去掉预留元素位置而已,而之后的元素size是这个ArrayList存放的最小size。
d:inexOF(Object)和lastIndexOf(Object)
public int indexOf(Object paramObject) {
int i;
if (paramObject == null)
for (i = 0; i < this.size; ++i)
if (this.elementData[i] == null)
return i;
else
for (i = 0; i < this.size; ++i)
if (paramObject.equals(this.elementData[i]))
return i;
return -1;
}
public int lastIndexOf(Object paramObject) {
int i;
if (paramObject == null)
for (i = this.size - 1; i >= 0; --i)
if (this.elementData[i] == null)
return i;
else
for (i = this.size - 1; i >= 0; --i)
if (paramObject.equals(this.elementData[i]))
return i;
return -1;
}
之所以把这两个方法放到一起分析是因为这两个方法在功能和实现上有很多类似的地方,indexOf()返回元素所在的下标位置,从第一个元素开始往后遍历,而lastIndexOf()方法则是相反的,则是最列表的最后一个元素开始遍历的 然后返回距离最后一个元素的位置。
以indexOf(Object)方法为例,通过源代码可以看到它的实现是这样的:如果传入的参数对象不为空的话,那么就开始从头到尾开始遍历这个List对象,如果所遍历到的元素对象不为空的话,那么就与传入的对象进行比较,如果比较的值相等那么就返回改比较的下标位置,如果没有找到这个元素就返回-1;
e:get(int)
public E get(int paramInt) {
rangeCheck(paramInt);
return elementData(paramInt);
}
E elementData(int paramInt) {
return this.elementData[paramInt];
}
private void rangeCheck(int paramInt) {
if (paramInt < this.size)
return;
throw new IndexOutOfBoundsException(outOfBoundsMsg(paramInt));
}
这个方法是根据索引下标返回对应位置的List中的值;它首先通过rangCheck(int)这个方法判断传入的参数是否合法,然后通过elementData(int)方法返回它的值;但是我们可以发现它其实是根据数组下标直接拿到对应的值的。
f:clear()
public void clear() {
this.modCount += 1;
for (int i = 0; i < this.size; ++i)
this.elementData[i] = null;
this.size = 0;
}
clear()顾名思义是把列表清空,它的实现逻辑很简单就是通过遍历然后将每个位置上的值设为null,并且把列表的长度设置为0;
g:删除元素
public E remove(int paramInt) {
rangeCheck(paramInt);
this.modCount += 1;
Object localObject = elementData(paramInt);
int i = this.size - paramInt - 1;
if (i > 0)
System.arraycopy(this.elementData, paramInt + 1, this.elementData,
paramInt, i);
this.elementData[(--this.size)] = null;
return localObject;
}
public boolean remove(Object paramObject) {
int i;
if (paramObject == null)
for (i = 0; i < this.size; ++i) {
if (this.elementData[i] != null)
continue;
fastRemove(i);
return true;
}
else
for (i = 0; i < this.size; ++i) {
if (!(paramObject.equals(this.elementData[i])))
continue;
fastRemove(i);
return true;
}
return false;
}
private void fastRemove(int paramInt) {
this.modCount += 1;
int i = this.size - paramInt - 1;
if (i > 0)
System.arraycopy(this.elementData, paramInt + 1, this.elementData,
paramInt, i);
this.elementData[(--this.size)] = null;
}
第一个方法remove(int)是通过下标位置删除元素的,首先是判断参数是否合法,然后获取这个下标位置的元素值,通过复制将每个元素的位置后移一下,最后将该原列表的最后面元素置为null,返回被删除的对象;第二个方法是remove(Object)直接删除对象,从第一个元素开始遍历,如果遍历到了就删除返回true,如果元素不存在就返回false,第三个方法是将删除元素的方法独立了根据下标删除对象元素。
h:添加元素
public boolean add(E paramE) {
ensureCapacityInternal(this.size + 1);
this.elementData[(this.size++)] = paramE;
return true;
}
public void add(int paramInt, E paramE) {
rangeCheckForAdd(paramInt);
ensureCapacityInternal(this.size + 1);
System.arraycopy(this.elementData, paramInt, this.elementData,
paramInt + 1, this.size - paramInt);
this.elementData[paramInt] = paramE;
this.size += 1;
}
public boolean addAll(Collection<? extends E> paramCollection) {
Object[] arrayOfObject = paramCollection.toArray();
int i = arrayOfObject.length;
ensureCapacityInternal(this.size + i);
System.arraycopy(arrayOfObject, 0, this.elementData, this.size, i);
this.size += i;
return (i != 0);
}
public boolean addAll(int paramInt, Collection<? extends E> paramCollection) {
rangeCheckForAdd(paramInt);
Object[] arrayOfObject = paramCollection.toArray();
int i = arrayOfObject.length;
ensureCapacityInternal(this.size + i);
int j = this.size - paramInt;
if (j > 0)
System.arraycopy(this.elementData, paramInt, this.elementData,
paramInt + i, j);
System.arraycopy(arrayOfObject, 0, this.elementData, paramInt, i);
this.size += i;
return (i != 0);
}
第一个和第二个方式添加一个元素,第三个和第四个方法是添加一个集合到列表中去;第一个方法是首先检查列表的大小是否合乎要求,然后直接在列表的末尾添加上元素。第二个方式在制定的位置添加上元素对象,它除了检查下标位置还有是列表的容量增加之外,就是将每个元素的位置后移,然后把空出来的位置留给我们需要插入的对象。第三个方法和第四个方法实现和第一个第二个类型,只不过把一个元素变成了一个集合。
i:contains(Object)
public boolean contains(Object paramObject) {
return (indexOf(paramObject) >= 0);
}
该方法是看是否含有某一元素,首先获取这个元素在列表中的位置,如果位置存在就说么含有该元素。
4.其他(小结)
ArrayList是在数组的基础了增加了一些我们开发中经常用到了方法,可是有些方法的效率也不怎么高,我们可以在具体的开发过程中视情况而定是否需要重写。不过它提供了大量的方法基本上够我们使用了。只是通过源码的解析了解一些它的底层方法的实现。
Java集合之ArrayList源码分析的更多相关文章
- Java集合干货——ArrayList源码分析
ArrayList源码分析 前言 在之前的文章中我们提到过ArrayList,ArrayList可以说是每一个学java的人使用最多最熟练的集合了,但是知其然不知其所以然.关于ArrayList的具体 ...
- 【Java集合】ArrayList源码分析
ArrayList是日常开发中经常使用到的集合,其底层采用数组实现,因此元素按序存放.其优点是可以使用下标来访问元素,时间复杂度是O(1).其缺点是删除和增加操作需要使用System.arraycop ...
- 死磕 java集合之ArrayList源码分析
欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. 简介 ArrayList是一种以数组实现的List,与数组相比,它具有动态扩展的能力,因此也可 ...
- 死磕 java集合之CopyOnWriteArraySet源码分析——内含巧妙设计
问题 (1)CopyOnWriteArraySet是用Map实现的吗? (2)CopyOnWriteArraySet是有序的吗? (3)CopyOnWriteArraySet是并发安全的吗? (4)C ...
- 死磕 java集合之LinkedList源码分析
问题 (1)LinkedList只是一个List吗? (2)LinkedList还有其它什么特性吗? (3)LinkedList为啥经常拿出来跟ArrayList比较? (4)我为什么把LinkedL ...
- 死磕 java集合之DelayQueue源码分析
问题 (1)DelayQueue是阻塞队列吗? (2)DelayQueue的实现方式? (3)DelayQueue主要用于什么场景? 简介 DelayQueue是java并发包下的延时阻塞队列,常用于 ...
- 死磕 java集合之PriorityBlockingQueue源码分析
问题 (1)PriorityBlockingQueue的实现方式? (2)PriorityBlockingQueue是否需要扩容? (3)PriorityBlockingQueue是怎么控制并发安全的 ...
- 死磕 java集合之PriorityQueue源码分析
问题 (1)什么是优先级队列? (2)怎么实现一个优先级队列? (3)PriorityQueue是线程安全的吗? (4)PriorityQueue就有序的吗? 简介 优先级队列,是0个或多个元素的集合 ...
- 死磕 java集合之LinkedHashSet源码分析
问题 (1)LinkedHashSet的底层使用什么存储元素? (2)LinkedHashSet与HashSet有什么不同? (3)LinkedHashSet是有序的吗? (4)LinkedHashS ...
随机推荐
- curl 要么 file_get_contents 获得授权页面的方法的必要性
今天,需要工作,需要使用 curl / file_get_contents 获得授权的必要性(Authorization)的页面内容.解决后写了这篇文章分享给大家. php curl 扩展,可以在se ...
- SQL SERVER 内存分配及常见内存问题(2)——DMV查询
原文:SQL SERVER 内存分配及常见内存问题(2)--DMV查询 内存动态管理视图(DMV): 从sys.dm_os_memory_clerks开始. SELECT [type] , SUM(v ...
- Python网络02 Python服务器进化
原文:Python网络02 Python服务器进化 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! **注意,在Python 3. ...
- POJ--2289--Jamie's Contact Groups【二分图的多个匹配+二分法答案】
链接:id=2289">http://poj.org/problem?id=2289 意甲冠军:有n个人,m个分组,每一个人能够分配到一些组别.问怎样分能使得人数最多的组别人数最少. ...
- SharePoint 2013 母版页修改后,无法添加应用程序
原文:SharePoint 2013 母版页修改后,无法添加应用程序 问题描述:前一段时间尝试了一下将HTML文件转换为母版页,但是,用着用着又发现新的问题,我们转换的母版页,设置成默认母版页以后,无 ...
- [Unity3D]Unity3D圣骑士当游戏开发商遭遇Mecanim动画系统
大家好.我是秦培.欢迎关注我的博客.我的博客地址blog.csdn.net/qinyuanpei. 博主总算赶在这个月底写出了这篇文章.这个月由于期末考试一直没时间研究太多关于技术方面 ...
- 酒旗少年狂暖风,至0基本的前端开发project教师们学习计划
酒旗风暖少年狂,为0基础前端开发project师做学习计划 夜幕降暂时.走到一张废弃已久的书桌前,打开台灯,看到书桌上已经布满灰尘,而桌上的那盆羸弱的文竹已经枝繁叶茂.我擦干净了桌面,坐了下来,把买回 ...
- 利用纯CSS3实现超立体的3D图片侧翻倾斜效果
原文:利用纯CSS3实现超立体的3D图片侧翻倾斜效果 上午的时候我在jQuery论坛上看到网友分享的一款CSS3 3D图片侧翻倾斜特效,觉得效果非常棒,其实话说回来,这玩意儿的实现真的非常简单,主要是 ...
- 实现双8bit数据指定的位置0要么1
<pre name="code" class="cpp"> 方法一 #include<stdio.h> #include<math ...
- oracle_分区表的新增、修改、删除、合并。普通表转分区表方法
一. 分区表理论知识Oracle提供了分区技术以支持VLDB(Very Large DataBase).分区表通过对分区列的判断,把分区列不同的记录,放到不同的分区中.分区完全对应用透明. Oracl ...