深入java集合学习2-ArrayList的实现原理
ArrayList概述
类概述
ArrayList是List 接口的大小可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。
每个 ArrayList 实例都有一个容量(capacity)。该容量是指用来存储列表元素的数组的大小。它大于或等于列表的大小。随着向 ArrayList 中不断添加元素,其容量也自动增长。在添加大量元素前,应用程序可以使用 ensureCapacity 操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。注意,此实现不是同步的。
类结构
ArrayList实现
底层采用数组实现
private transient Object[] elementData;
看到这行代码,或许有很多同学和我刚开始一样。为什么ArrayList源代码中要将elementData定义为transient变量呢?这个问题的研究大家可以看这篇文章【http://kakaluyi.iteye.com/blog/870137】.如果对java中的序列化知识不懂的可以看本博客中的关于序列化的文章。【理解Java对象序列化】 【java中可定制的序列化过程 writeObject与readObject】 【Java中的序列化Serialable高级详解【转载】】
可变数组
int array1[]={1,2,3};//定义一个数组长度为3的整形数组,此时数组的三个元素分别为1,2,3
此时如果想添加一个元素"4"到数组中去,因为数组长度是不可变的,无法直接array[3]=4;那么此时只能借助另外一个长度为4的数组array2,将数组array1的元素拷贝到新数组array2中,再赋值array[3]=4;然后将array1的引用指向array2。此时array1的表现形式就像是可变数组了.
int array1[]={1,2,3};
int array2[]=new int[4];
for(int i=0;i<array1.length;i++){
array2[i]=array1[i];
}
array2[3]=4;
array1=array2;
此时我们就可以想到,要想动态的实现ArrayList的可变数组,那么在ArrayList初始化的时候我们要初始化一个容量,往ArrayList中添加元素时我们要动态的改变底层数组的长度。那我们来看看jdk源码是怎么初始化和动态改变数组容量的
初始化
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
this.elementData = new Object[initialCapacity];
} public ArrayList() {
this(10);
} public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
从源码中我们可以得知ArrayList提供了三种方式的构造器
动态调整底层数组的容量
public void ensureCapacity(int minCapacity) {
modCount++;//Fast-Fail机制(具体机制本文章中专门有说的)
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3) / 2 + 1;
if (newCapacity < minCapacity)
newCapacity = minCapacity;
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
从源码中我们可知动态调整容量的过程是
从上述分析可知如果可预知数据量的多少,可在构造ArrayList时指定其容量。在添加大量元素前,应用程序也可以使用ensureCapacity操作来增加ArrayList实例的容量,这可以减少递增式再分配的数量。这样可以减少内存的占用和提高存储效率。
集合操作
add添加元素
元素添加到列表的尾部
public boolean add(E e) {
ensureCapacity(size + 1); //动态调整底层数组的容量
elementData[size++] = e;//将元素添加到列表尾部,列表大小加1
return true;//返回true,添加成功
}
元素添加到指定位置
public void add(int index, E element) {
if (index > size || index < 0)// 首先检查index是否合法,要保证不能大于列表的实际长度,且index不能为负数,如果不满足就抛出异常
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
ensureCapacity(size + 1);// 添加元素的操作都要动态的调整底层数组的容量
System.arraycopy(elementData, index, elementData, index + 1, size - index);//调用System的数组拷贝方法
elementData[index] = element;// 设置新数组处index处的值为指定的元素
size++;// 列表长度加1
}
集合中的元素添加到列表尾部
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacity(size + numNew);
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
从指定位置开始添加集合中元素
public boolean addAll(int index, Collection<? extends E> c) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); Object[] a = c.toArray();
int numNew = a.length;
ensureCapacity(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;
}
get获取元素
public E get(int index) {
RangeCheck(index);// 检查index是否合法 return (E) elementData[index];// 返回指定index处的元素
}
ArrayList中有一私有方法,来检测index是否合法。
private void RangeCheck(int index) {
//如果index不小于列表的元素个数,就抛出异常
if (index >= size)
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
remove删除元素
删除指定位置处的元素
public E remove(int index) {
RangeCheck(index);// 检查index是否合法 modCount++;// Fail-Fast机制
E oldValue = (E) elementData[index];// 获取待删除的元素 int numMoved = size - index - 1;//移动元素个数
if (numMoved > 0) {
System.arraycopy(elementData, index + 1, elementData, index, numMoved);//向前移动元素
}
elementData[--size] = null;//将原来size-1处的元素设置为空,列表大小-1
return oldValue;//返回删除的元素
}
删除首次出现的指定元素
ArrayList允许重复元素的存在,此方法删除的是首次出现的指定元素,要想删除指定元素内部实现过程是:(1)循环遍历列表中找到指定元素首次出现的位置 (2)删除此位置的元素,那么这样就类似remove(int index)方法。jdk内部实现代码如下:
// 删除首次出现的指定元素,元素存在返回 true,元素不存在返回false
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
} // ArrayList的私有方法,与remove(int index)方法类似
private void fastRemove(int index) {
modCount++;// Fail-Fast机制
int numMoved = size - index - 1;
if (numMoved > 0) {
System.arraycopy(elementData, index + 1, elementData, index, numMoved);
}
elementData[--size] = null;
}
删除指定位置区间内的元素
// 移除列表中索引在 fromIndex(包括)和 toIndex(不包括)之间的所有元素
protected void removeRange(int fromIndex, int toIndex) {
modCount++;
int numMoved = size - toIndex;
System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved);
int newSize = size - (toIndex - fromIndex);
while (size != newSize)
elementData[--size] = null;
}
为什么jdk源码中此方法是protected呢?具体的解释请参考
ArrayList removeRange方法分析这篇博文。
深入java集合学习2-ArrayList的实现原理的更多相关文章
- Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- 2019/3/4 java集合学习(二)
java集合学习(二) 在学完ArrayList 和 LinkedList之后,基本已经掌握了最基本的java常用数据结构,但是为了提高程序的效率,还有很多种特点各异的数据结构等着我们去运用,类如可以 ...
- Java集合学习(9):集合对比
一.HashMap与HashTable的区别 HashMap和Hashtable的比较是Java面试中的常见问题,用来考验程序员是否能够正确使用集合类以及是否可以随机应变使用多种思路解决问题.Hash ...
- 转:深入Java集合学习系列:HashSet的实现原理
0.参考文献 深入Java集合学习系列:HashSet的实现原理 1.HashSet概述: HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持.它不保证set 的迭代顺序:特 ...
- 2019/3/2周末 java集合学习(一)
Java集合学习(一) ArraysList ArraysList集合就像C++中的vector容器,它可以不考虑其容器的长度,就像一个大染缸一 样,无穷无尽的丢进去也没问题.Java的数据结构和C有 ...
- java集合系列之ArrayList源码分析
java集合系列之ArrayList源码分析(基于jdk1.8) ArrayList简介 ArrayList时List接口的一个非常重要的实现子类,它的底层是通过动态数组实现的,因此它具备查询速度快, ...
- Java集合框架之ArrayList浅析
Java集合框架之ArrayList浅析 一.ArrayList综述: 位于java.util包下的ArrayList是java集合框架的重要成员,它就是传说中的动态数组,用MSDN中的说法,就是Ar ...
- java集合学习(2):Map和HashMap
Map接口 java.util 中的集合类包含 Java 中某些最常用的类.最常用的集合类是 List 和 Map. Map 是一种键-值对(key-value)集合,Map 集合中的每一个元素都包含 ...
- Java IO学习笔记:概念与原理
Java IO学习笔记:概念与原理 一.概念 Java中对文件的操作是以流的方式进行的.流是Java内存中的一组有序数据序列.Java将数据从源(文件.内存.键盘.网络)读入到内存 中,形成了 ...
- java集合系列之三(ArrayList)
上一章,我们学习了Collection的架构.这一章开始,我们对Collection的具体实现类进行讲解:首先,讲解List,而List中ArrayList又最为常用.因此,本章我们讲解ArrayLi ...
随机推荐
- 最好的5个Android ORM框架
在开发Android应用时,保存数据有这么几个方式, 一个是本地保存,一个是放在后台(提供API接口),还有一个是放在开放云服务上(如 SyncAdapter 会是一个不错的选择). 对于第一种方式, ...
- HTML5移动开发学习笔记之CSS3基础学习
CSS回顾 在学CSS3之前首先巩固下CSS的基础知识. 1.CSS框模型 举例子: #box { width: 70px; margin: 10px; padding: 5px; } 这个代码将出现 ...
- 【.NET深呼吸】基于异步上下文的本地变量(AsyncLocal)
在开始吹牛之前,老周说两个故事. 第一个故事是关于最近某些别有用心的人攻击.net的事,其实我们不用管它们,只要咱们知道自己是.net爱好者就行了,咱们就是因为热爱.net才会选择它.这些人在这段时间 ...
- Python模拟登陆新浪微博
上篇介绍了新浪微博的登陆过程,这节使用Python编写一个模拟登陆的程序.讲解与程序如下: 1.主函数(WeiboMain.py): import urllib2 import cookielib i ...
- 前端学HTTP之网站架构演化
前面的话 本文将详细介绍网站架构的演化过程 初始阶段 大型网站都是从小型网站发展而来,网站架构也是一样,是从小型网站架构逐步演化而来.小型网站最开始时没有太多人访问,只需要一台服务器就绰绰有余,这时的 ...
- 设计模式(一):“穿越火线”中的“策略模式”(Strategy Pattern)
在前段时间呢陆陆续续的更新了一系列关于重构的文章.在重构我们既有的代码时,往往会用到设计模式.在之前重构系列的博客中,我们在重构时用到了“工厂模式”.“策略模式”.“状态模式”等.当然在重构时,有的地 ...
- CRL快速开发框架系列教程八(使用CRL.Package)
本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...
- DM9000驱动移植在mini2440(linux2.6.29)和FS4412(linux3.14.78)上的实现(deep dive)篇一
关于dm9000的驱动移植分为两篇,第一篇在mini2440上实现,基于linux2.6.29,也成功在在6410上移植了一遍,和2440非常类似,第二篇在fs4412(Cortex A9)上实现,基 ...
- Lucene的评分(score)机制研究
首先,需要学习Lucene的评分计算公式—— 分值计算方式为查询语句q中每个项t与文档d的匹配分值之和,当然还有权重的因素.其中每一项的意思如下表所示: 表3.5 评分公式中的因子 评分因子 描 述 ...
- 推荐13款javascript模板引擎
javaScript 在生成各种页面内容时如果能结合一些模板技术,可以让逻辑和数据之间更加清晰,本文介绍 X 款 JavaScript 的模板引擎.(排名不分先后顺序) 1. Mustache 基于j ...