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 ...
随机推荐
- C# WinForm中实现CheckBox全选反选功能
今天一群里有人问到这个功能,其实应该挺简单,但提问题的人问题的出发点并没有描述清楚.因此,一个简简单单的需求,就引起了群内热烈的讨论.下面看看这个功能如何去实现,先上效果: 下面直接上代码,请不要在意 ...
- Dynamic proxy
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflec ...
- Android布局解析,图文(转)
LinearLayout:相当于Java GUI中的FlowLayout(流式布局),就是说一个组件后边跟一个,挨着靠,一个组件把一行占满了,就靠到下一行. linearlayoutdemo.xml ...
- .NET单元测试艺术(1) - 单元测试的基本知识
List 1.1 一个要测试的SimpleParser类 using System; namespace AOUT.CH1.Examples { public class SimpleParser { ...
- [Django1.6]The MEDIA_ROOT and STATIC_ROOT settings must different 解决
该项目有一个图片上传功能,为了把上传路径很简单,写在同一个静态文件路径,于wi7执行机器上没问题,今centos我们报道了机上,如下面的错误: django.core.exceptions.Impro ...
- 海哥:T2C时代的到来了,那么什么叫T2C?
昨天写了一篇文章叫做<我为什么选择家具行业,以及T2C概念的创办>,地址:http://user.qzone.qq.com/198819880/blog/1414399801 ,里面提到了 ...
- ASP.NET MVC导出excel
ASP.NET MVC导出excel 要在ASP.NET MVC站点上做excel导出功能,但是要导出的excel文件比较大,有几十M,所以导出比较费时,为了不影响对界面的其它操作,我就采用异步的方式 ...
- 好记心不如烂笔头,ssh登录 The authenticity of host 192.168.0.xxx can't be established. 的问题
用ssh登录一个机器(换过ip地址),提示输入yes后,屏幕不断出现y,仅仅有按ctrl + c结束 错误是:The authenticity of host 192.168.0.xxx can't ...
- Session or Cookie?是否有必要使用Tomcat等一下Web集装箱Session
Cookie是HTTP协议标准下的存储用户信息的工具.浏览器把用户信息存放到本地的文本文件里. Session是基于Cookie实现的. 2011年4月,武汉群硕面试的时候(实习生).面试官也问过这个 ...
- Nyoj 天下第一(spfa)
描述 AC_Grazy一直对江湖羡慕不已,向往着大碗吃肉大碗喝酒的豪情,但是“人在江湖漂,怎能 不挨刀",”人在江湖身不由己",如果自己的武功太差,在江湖会死的很惨,但是AC_Gr ...