JDK源码阅读—ArrayList的实现
1 继承结构图
ArrayList继承AbstractList,实现了List接口

2 构造函数
transient Object[] elementData; // 数组保存元素 private int size; // 记录长度
size记录ArrayList的长度,elementData记录元素的值
transient是Java语言的关键字,用来表示一个域不是该对象串行化的一部分。当一个对象被串行化的时候,transient型变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进去的。transient详细介绍如链接:transient关键字介绍
三种构造方法
public ArrayList(int initialCapacity)
参数为长度,必须为大于等于0整数,如果传入0,与第二种构造方法结果相同,都是建立一个空的Object数组
public ArrayList()
创建一个空的Object数组
public ArrayList(Collection <?extendsE> c)
如果参数不为空,则使用Collection的toArray方法,把c转为对象数组,否则创建一个空的Object数组
3 需要注意的方法
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
常使用的是set(E element)这个方法 set方法如果使用set(int index, E element)这个方法,index对应的位置如果已经有值,该值会被新set的对象替换,并且返回旧值
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 element),如果使用了add(int index, E element)这个方法,从上图的实现来看,它会先把记录元素的数组长度加1,然后把index之后的元素全部都后移一位,然后把新插入的元素放在索引为index的位置
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
remove(int index)的删除方法是把index之后的元素都往前移动一个位置,所以如果你用for循环遍历ArrayList并且在循环中满足某个条件就remove掉一个元素,那么就会抛出IndexOutOfBoundsException
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;
}
Remove(Object o)会从前往后查找,找到第一个相等的对象进行删除,删除的原理也是后面的元素前移一位,所以遍历ArrayList最好用迭代器进行遍历。
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(int index, Collection c)如果当前ArrayList的长度小于index,直接从index开始,把c的元素按顺序摆放,如果当前ArrayList的长度大于Index,则把c插入到当前ArrayList的index 到index+c.length的位置,再把原ArrayList的剩余元素接到后面
4 不常用方法
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
batchRemove不常用方法,可以批量删除当前list中包含(complement == true)或不包含(complement == false)某个集合c中所有元素。并返回操作的结果。
备注
上面说到了,直接使用ArrayList的remove在遍历中很容易出错,下面看看它的迭代器是怎么实现remove的
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
cursor记录要访问的下一个index,lastRet记录当前已经读到的index,迭代器在remove的时候,先调用ArrayList的remove方法删除对应元素,同时,会把cursor和lastRet都减1,自动帮我们完成了元素的重新定位。
本文来自我的个人博客:http://blog.duchangchun.com/2018/12/29/jdk_arraylist/
JDK源码阅读—ArrayList的实现的更多相关文章
- JDK源码阅读——ArrayList
序 如同C语言中字符数组向String过渡一样,作为面向对象语言,自然而然的出现了由Object[]数据形成的集合.本文从JDK源码出发简单探讨一下ArrayList的几个重要方法. Fields / ...
- JDK源码阅读--ArrayList
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess ...
- JDK源码阅读(三):ArraryList源码解析
今天来看一下ArrayList的源码 目录 介绍 继承结构 属性 构造方法 add方法 remove方法 修改方法 获取元素 size()方法 isEmpty方法 clear方法 循环数组 1.介绍 ...
- JDK源码阅读-FileDescriptor
本文转载自JDK源码阅读-FileDescriptor 导语 操作系统使用文件描述符来指代一个打开的文件,对文件的读写操作,都需要文件描述符作为参数.Java虽然在设计上使用了抽象程度更高的流来作为文 ...
- JDK源码阅读(一):Object源码分析
最近经过某大佬的建议准备阅读一下JDK的源码来提升一下自己 所以开始写JDK源码分析的文章 阅读JDK版本为1.8 目录 Object结构图 构造器 equals 方法 getClass 方法 has ...
- 利用IDEA搭建JDK源码阅读环境
利用IDEA搭建JDK源码阅读环境 首先新建一个java基础项目 基础目录 source 源码 test 测试源码和入口 准备JDK源码 下图框起来的路径就是jdk的储存位置 打开jdk目录,找到sr ...
- JDK源码阅读-FileOutputStream
本文转载自JDK源码阅读-FileOutputStream 导语 FileOutputStream用户打开文件并获取输出流. 打开文件 public FileOutputStream(File fil ...
- JDK源码阅读-FileInputStream
本文转载自JDK源码阅读-FileInputStream 导语 FileIntputStream用于打开一个文件并获取输入流. 打开文件 我们来看看FileIntputStream打开文件时,做了什么 ...
- JDK源码阅读-ByteBuffer
本文转载自JDK源码阅读-ByteBuffer 导语 Buffer是Java NIO中对于缓冲区的封装.在Java BIO中,所有的读写API,都是直接使用byte数组作为缓冲区的,简单直接.但是在J ...
随机推荐
- How to use ftp in a shell script
转载How to use ftp in a shell script How to use ftp in a shell script Bruce EdigerBruce Ediger's home ...
- 【t010】最近距离
Time Limit: 1 second Memory Limit: 32 MB [问题描述] 聚类方法要求将空间中的点集,按照一点的方式进行归类,要求每一类中的点集相互之间的距离足够的"近 ...
- 复制相关参数学习笔记--slave上的参数
server_id server_uuid relay_log io_thread 读取过来的本地日志. relaylog文件名前缀,可以是全路径. relay_log_index relay ...
- [Grid Layout] Specify a grid gutter size with grid-gap
It’s beautifully straightforward to add a gutter to our grid layout. Let’s apply one with grid-gap.
- 一步步学习Linux多任务编程
系统调用 01.什么是系统调用? 02.Linux系统调用之I/O操作(文件操作) 03.文件描述符的复制:dup(), dup2() 多进程实现多任务 04.进程的介绍 05.Linux可执行文件结 ...
- 第二篇:_UICascadingTextStorage attributesAtIndex:effectiveRange:]: Range or index out of bounds
注意下文标红字段: #pragma mark- 输入改变时 - (void) textFieldDidChanged:(UITextField *) TextField{ //搜索关键字一旦改变,将重 ...
- ITFriend创业败局(五):创业可以停止,公司必须注销,不然后果很严重
马上又要继续出来创业做事了,想到要注册公司,有个麻烦事. 事情得回到2014年9月. 当时,由于各种因素,决定放弃ITFriend,当然也放弃了原来了公司.先是,咨询了横德瑞的陈总,关于公司注销的事. ...
- 7个在线学习C++编程的最佳途径
7个在线学习C++编程的最佳途径 jopen 2012-09-27 15:51:13 • 发布 摘要:C++是一种静态数据类型检查的.支持多重编程范式的通用程序设计语言.它支持过程化程序设计.数据抽象 ...
- [Ramda] Change Object Properties with Ramda Lenses
In this lesson we'll learn the basics of using lenses in Ramda and see how they enable you to focus ...
- Git 常用命令总结(精问)
Git 常用命令总结(精问) 一.总结 一句话总结:下次尝试做任何git的操作之前,把这个精看精问一遍,绝对会有意向不到的收获. 二.Git 常用命令总结 1.git常用命令 安装及配置: Ubunt ...