【java集合框架源码剖析系列】java源码剖析之ArrayList
注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本。
本博客将从源码角度带领大家学习关于ArrayList的知识。
一ArrayList类的定义:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
从上述代码可以看到:ArrayList继承自AbstractList同时实现了List,RandomAccess,Cloneable与Serializable接口
二ArrayList类一些重要属性:
private static final int DEFAULT_CAPACITY = 10; private static final Object[] EMPTY_ELEMENTDATA = {}; private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; transient Object[] elementData; private int size;
从这里可以看到ArrayList全部操作是基于Object[]数据结构的,即ArrayList底层是基于数组实现的,因此具备较好的随机访问的能力
三ArrayList内部实现原理:我们先来看一下ArrayList的构造器
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 {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
可以看到当用一个集合作为参数去初始化一个ArrayList的时候,其内部处理过程与LinkedList非常类似,即先将集合转化为数组,然后将元素复制到自己内部定义的数组elementData数组中。返回的若不是Object[]将调用Arrays.copyOf方法将其转为Object[]。Arrays.copyOf代码如下:
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
四ArrayList一些重要的函数:
1add(E e)与add(int index,E element)
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
} public void add(int index, E element) {
rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
其中add(E e)表示在ArrayList的末尾添加一个元素,而add(int index,E element)表示在index处添加一个元素,其中为了防止当数组中的容量不够的情况,会调用ensureCapacityInternal(size + 1); 函数用来确保向数组中添加元素的时候数组大小始终足够。代码如下;
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
} ensureExplicitCapacity(minCapacity);
}
而在该函数中调用了ensureExplicitCapacity(int minCapacity)函数,我们来看一下其源码:
private void ensureExplicitCapacity(int minCapacity) {
modCount++; // overflow-conscious code
if (minCapacity - elementData.length > 0)// 超出了数组可容纳的长度,则进行动态扩展,即调用grow函数
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);//将oldCapacity除以2在加上自己,即新的容量为原来的1.5倍
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);
}
综上,当调用add()添加元素时,add方法先调用ensureCapacityInternal方法增加自身容量(注意此时仅仅是minCapicity的值加1,而数组内存还未扩容),本质上是通过grow()函数来完成的,在调用grow()函数时,先求出原数组的长度记为oldCapacity,然后将该值更新为oldCapacity+oldCapacity>>1即新的容量为原来容量的1.5倍.,将其记为newCapacity,如果扩容后的容量超出了数组可容纳的最大长度MAX_ARRAY_SIZE,则调用hugeCapacity()将其返回值 Integer.MAX_VALUE作为新的容量,然后调用系统的arraycopy函数将原数组的内容复制到新数组中返回。
即add()方法中存在扩容机制,当通过add()方法添加一个元素时,会先调用ensureCapacityInternal方法来确保数组内存始终足够,本质上是通过grow函数来完成的,在grow()函数中会将原数组的长度更改为其原来的1.5倍,如果更改后的数组容量比传入的minCapacity小,则将更改后的容量更新为minCapacity(该值是期望的数组的最小容量,如当已存在1个元素然后添加一个元素则minCapacity的值为2即能容纳添加进去的元素的最小容量,扩容后的值newCapacity在绝大多数情况下比它大,但可能比它小,因为上述扩容机制采用的是扩大为1.5倍,而采用的是整除,如1+1/2=1,而minCapacity=size+1=2,此时newCapacity<minCapacity)
2addAll(Collection<? extends E> c) 与addAll(int index, Collection<? extends E> c)
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;
} 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;
}
可以看到,addAll()与add()方法几乎完全相同,只不过存在一个将集合转化为数组的过程,这一点与LinkedList非常类似,然后接下来的操作与add操作完全相同,即
调用ensureCapacityInternal扩容,如果插入位置不是Arraylist的末尾,则调用系统的arraycopy函数将索引处及其后的元素后移一位在index处插入该元素。处理完毕后将临时数组a中的元素arraycopy到elementData中。
3Object[] toArray()
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
} public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
该函数的作用就是返回一个包含list中所有元素的数组,具体实现过程如下:
如果传入数组的长度length小于size,返回一个新的数组,大小为size,类型与传入数组相同;
否则将elementData的元素全部复制到传入的数组a中,并返回a;
若length大于size,除了复制elementData外,还将把返回数组的第size个元素置为空。
4void sort(Comparator<? super E> c)
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
可以看到该sort函数是基于Comparator<? super E> c接口的,即待排序的元素必须实现Comparator接口,其实不仅仅是ArrayList,java集合框架中的全部集合的排序都是基于
Comparator接口的。
五总结:
1ArrayList的内部是基于Object[]类型的数组实现的,支持下标访问,因此具备较高的随机访问速度。
2当用一个集合作为参数来构造一个ArrayList时,其内部是是先将该集合转化为数组,这一点与LinkedList非常类似,与HashMap一样,ArrayList也存在扩容机制,即调用ensureCapacityInternal扩容
3可以看到ArrayList中的方法都未使用synchronized关键字修饰,即ArrayList是非同步的。
4ArrayList允许重复元素存在,因为在add元素的过程中不存在HashMap中put时判重替换的过程,只是进行简单的插入操作。
【java集合框架源码剖析系列】java源码剖析之ArrayList的更多相关文章
- [转载] Java集合框架之小结
转载自http://jiangzhengjun.iteye.com/blog/553191 1.Java容器类库的简化图,下面是集合类库更加完备的图.包括抽象类和遗留构件(不包括Queue的实现): ...
- Java集合框架体系JCF
Java 集合框架体系作为Java 中十分重要的一环, 在我们的日常开发中扮演者十分重要的角色, 那么什么是Java集合框架体系呢? 在Java语言中,Java语言的设计者对常用的数据结构和算法做了一 ...
- Java集合框架使用总结
Java集合框架使用总结 前言:本文是对Java集合框架做了一个概括性的解说,目的是对Java集合框架体系有个总体认识,如果你想学习具体的接口和类的使用方法,请参看JavaAPI文档. 一.概述数据结 ...
- 【java集合框架源码剖析系列】java源码剖析之TreeSet
本博客将从源码的角度带领大家学习TreeSet相关的知识. 一TreeSet类的定义: public class TreeSet<E> extends AbstractSet<E&g ...
- 【java集合框架源码剖析系列】java源码剖析之HashSet
注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本.本博客将从源码角度带领大家学习关于HashSet的知识. 一HashSet的定义: public class HashSet&l ...
- 【java集合框架源码剖析系列】java源码剖析之TreeMap
注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本.本博客将从源码角度带领大家学习关于TreeMap的知识. 一TreeMap的定义: public class TreeMap&l ...
- 【java集合框架源码剖析系列】java源码剖析之LinkedList
注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本. 在实际项目中LinkedList也是使用频率非常高的一种集合,本博客将从源码角度带领大家学习关于LinkedList的知识. ...
- 【java集合框架源码剖析系列】java源码剖析之HashMap
前言:之所以打算写java集合框架源码剖析系列博客是因为自己反思了一下阿里内推一面的失败(估计没过,因为写此博客已距阿里巴巴一面一个星期),当时面试完之后感觉自己回答的挺好的,而且据面试官最后说的这几 ...
- 【java集合框架源码剖析系列】java源码剖析之java集合中的折半插入排序算法
注:关于排序算法,博主写过[数据结构排序算法系列]数据结构八大排序算法,基本上把所有的排序算法都详细的讲解过,而之所以单独将java集合中的排序算法拿出来讲解,是因为在阿里巴巴内推面试的时候面试官问过 ...
随机推荐
- 谷歌开源的TensorFlow Object Detection API视频物体识别系统实现教程
视频中的物体识别 摘要 物体识别(Object Recognition)在计算机视觉领域里指的是在一张图像或一组视频序列中找到给定的物体.本文主要是利用谷歌开源TensorFlow Object De ...
- Linux下用程序实现统计cpu和内存的利用率
Linux下没有直接可以调用系统函数知道CPU占用和内存占用.那么如何知道CPU和内存信息呢.只有通过proc伪文件系统来实现. proc伪文件就不介绍了,只说其中4个文件.一个是/proc/stat ...
- Cloud TPU Demos(TensorFlow 云 TPU 样例代码)
Cloud TPU Demos 这是一个Python脚本的集合,适合在开源TensorFlow和 Cloud TPU 上运行. 如果您想对模型做出任何修改或改进,请提交一个 PR ! https:// ...
- 在java中String类为什么要设计成final
在java中String类为什么要设计成final? - 胖胖的回答 - 知乎 https://www.zhihu.com/question/31345592/answer/114126087
- instanceof的用法
转载自:http://blog.csdn.net/cnmilan/article/details/41696093package myPackage; /** * instanceof运算符用法 * ...
- Safari 3D transform变换z-index层级渲染异常
(猛戳来源:http://www.zhangxinxu.com/wordpress/?p=5569)
- 有些时候会看到url参数上出现%BF之类
这是URLDecoder和URLEncoder的原因 因为他们是参数,避免影响网页的连接跳转,再到了服务器的时候会自动转过来 当URL地址中仅包含普通非中文字符串和application/x-www- ...
- HashSet<T>的妙用
HashSet<int> hs = new HashSet<int>(); var ret = hs.Add(1); //ret==true var ret2 = hs.Ad ...
- OLE:对象的类没有在注册数据库中注册
我在网上下载了破解版的SAS9.3,用了一段时间之后,今天打开就填出一个提示框:OLE:对象的类没有在注册数据库中注册 激活该对象所需的应用程序不可用.是否用"转换--"将其转换为 ...
- python学习之路前端-JavaScript
JavaScript简介 JavaScript一种直译式脚本语言,是一种动态类型.弱类型.基于原型的语言,内置支持类型.它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本 ...