Java ArrayList 详解
只记录目前为止关注的。JDK1.8
一、基础属性
1.1 内部参数
//空存储实例。直接new ArrayList()便是以该空数组作为实例
private static final Object[] EMPTY_ELEMENTDATA = {};
//默认容量大小,在由空实例进行首次扩容时,扩到到该长度。
//实际使用中,并未实际存在Capacity这个参数,需要扩容时直接根据旧数组的length进行扩容
private static final int DEFAULT_CAPACITY = 10;
//实际存储数组
transient Object[] elementData;
//存储元素个数
private int size;
//该字段用于迭代器的fail-fast机制【参考另一篇文章】
protected transient int modCount = 0;
1.2 三个重载构造方法
//构建一个空实例,elementData指向容量为0的空数组
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//以给定容量初始化存储数组
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);
}
}
//以集合初始化创建列表
//步骤:1. 调用toArray(),将给定集合转成数组(以ArrayList为例,toArray()返回的是实际存在元素的那部分数组,即[0,size))
//2. 让size直接等于给定集合的数组长度
//3. 判断size如果为0则直接创建空存储实例,否则使用Arrays.copyOf将给定集合数组复制一份(浅拷贝),作为存储数组
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 的 toArray()源码,复制[0,size)部分返回
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
二、操作及策略
2.1 动态扩容
扩容策略:当数组全满了才扩容,新长度=旧长度*1.5
动态扩容有两个入口:供用户调用的显式扩容ensureCapacity()和添加元素时的隐式扩容ensureCapacityInternal(),不过均是调用ensureExplicitCapacity()来根据传入所需容量值决定是否扩容,最终实际扩容操作在grow()方法中。
//显式扩容入口方法
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
//隐式扩容入口方法
//其中参数 minCapacity 值为 size+1,由add方法调用传入
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//添加方法
public boolean add(E e) {
// 传入最小所需容量为size+1
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
//计算所需容量,实则不需要计算
//只是单纯判断当前是否是空实例,为空就话返回 "默认容量"与minCapacity之间的较大值,不为空直接返回minCapacity
//参数 minCapacity 只有两种情况:
// 1. 隐式扩容时,如add()传入,其值为 size+1
// 2. 显式扩容,用户指定
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
//在此处,根据最小所需容量来判断是否实际进行扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0) //若已经占满,才进行扩容
grow(minCapacity);
}
//实际扩容方法
//可以看到扩容策略为:length + length*0.5(右移1位缩小1倍)
//然后调用Arrays.copyOf浅复制
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); //旧长度+旧长度*2
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);
}
2.2 删除操作
ArrayList中有两个remove方法public E remove(int index)和public boolean remove(Object o)
源码实现得其实挺简单,本来还在犹豫有没有必要把remove方法记录下来,但发现有个点值得注意以下:
注意点:JDK对于数组内元素统一移动操作,偏好使用System.arraycopy(ary, srcPos, ary, destPos, len)来移动
//通过给定下标来删除元素,并返回删除元素
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); // 使用System.arraycopyl来移动数组元素
elementData[--size] = null; // clear to let GC do its work // 删除后size-1,并将先前最后一个元素置null
return oldValue; //返回删除元素
}
// 判断下标是否合理(<size),否则抛出异常
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// 删除首个与给定元素相等的元素,元素判等使用equal方法
// 成功返回true,失败返回false
public boolean remove(Object o) {
if (o == null) { // 可以删除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])) { // 从头开始一个个检查,使用equal判等
fastRemove(index);
return true;
}
}
return false;
}
/*
* 官方注释说得很清楚了:删除元素,但跳过边界检查且不返回删除元素。(就是remove(index)的缩略版
* Private remove method that skips bounds checking and does not
* return the value removed.
*/
private void fastRemove(int index) {
modCount++;
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
}
Java ArrayList 详解的更多相关文章
- Java集合详解1:一文读懂ArrayList,Vector与Stack使用方法和实现原理
本文非常详尽地介绍了Java中的三个集合类 ArrayList,Vector与Stack <Java集合详解系列>是我在完成夯实Java基础篇的系列博客后准备开始写的新系列. 这些文章将整 ...
- Java synchronized 详解
Java synchronized 详解 Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 1.当两个并发线程访问同一个对象object ...
- java集合详解
1.java集合框架的层次结构 Collection接口: Set接口: HashSet具体类 LinkedHashSet具体类 TreeSet具体类 List接口: ArrayList具体类 L ...
- 【集合框架】JDK1.8源码分析之ArrayList详解(一)
[集合框架]JDK1.8源码分析之ArrayList详解(一) 一. 从ArrayList字表面推测 ArrayList类的命名是由Array和List单词组合而成,Array的中文意思是数组,Lis ...
- java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一
对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下. 本文参考java 泛型详解.Java中的泛型方法. java泛型详解 1. 概述 泛型在 ...
- Java List详解,面试中应该如何解答关于List的问题
对于面试,我们在介绍Java的List的时候,一般需要介绍到,什么是List?List包括什么?各自在用法上有什么区别,在存储上有什么区别?List需要注意什么?把这些问题串起来,我们可以这样介绍: ...
- Java集合详解8:Java的集合类细节精讲
Java集合详解8:Java集合类细节精讲 今天我们来探索一下Java集合类中的一些技术细节.主要是对一些比较容易被遗漏和误解的知识点做一些讲解和补充.可能不全面,还请谅解. 本文参考:http:// ...
- Java集合详解3:Iterator,fail-fast机制与比较器
Java集合详解3:Iterator,fail-fast机制与比较器 今天我们来探索一下LIterator,fail-fast机制与比较器的源码. 具体代码在我的GitHub中可以找到 https:/ ...
- Java泛型详解(转)
文章转自 importNew:Java 泛型详解 引言 泛型是Java中一个非常重要的知识点,在Java集合类框架中泛型被广泛应用.本文我们将从零开始来看一下Java泛型的设计,将会涉及到通配符处理 ...
随机推荐
- 「新手必看」Python+Opencv实现摄像头调用RGB图像并转换成HSV模型
在ROS机器人的应用开发中,调用摄像头进行机器视觉处理是比较常见的方法,现在把利用opencv和python语言实现摄像头调用并转换成HSV模型的方法分享出来,希望能对学习ROS机器人的新手们一点帮助 ...
- loj2026 「JLOI / SHOI2016」成绩比较
orz #include <iostream> #include <cstdio> using namespace std; typedef long long ll; int ...
- Java学习笔记2---设置环境变量JAVA_HOME,CLASSPATH,PATH
1.环境变量包括: JAVA_HOME,CLASSPATH,PATH 2.设置环境变量的目的: 路径搜索,方便查找到jdk的安装路径.方便搜索用到的类文件.方便搜索用到的可执行文件如java,java ...
- oracle 可以连接数据库,vs连不上. 报错提示:ORA-12154: TNS: 无法解析指定的连接标识符
方法1:问题:VS 连接 Data Source=ORCL_Service19;User Id=*;Password=* 连接不上 oracle 可以连接数据库,vs连不上,报错提示:ORA-1215 ...
- Redis 使用多个数据库及密码配置
redis的默认端口是6379,可以使用的数据库最多有16个,不同数据库之间是独立的, 可以通过 select num 的方式访问不同的数据库 可以通过下面的命令来切换到不同的数据库下,每个数据库都有 ...
- 轻量级的C++插件框架 - X3 C++ PluginFramework
X3 C++ PluginFramework 代号为X3的C++轻量级通用插件框架平台是一套通用的C++轻量级插件体系,没有使用MFC.ATL.COM.可在Windows和Linux下编译运行.应用程 ...
- JAVA-json数据与Java的bean类的互相转换
Java调用webservice时用到了json格式的数据,然后就整理这个类.那里不合适的话,希望能够得到指正. public class JsonsAndBeanList { /** * json数 ...
- sqlserver的left join优化
MS sqlserver 对4张表进行left join join字段是varchar类型长度20,也都建了索引,但是光查一个count(Id) 耗时就超过了8秒,数据量只有100多万条,该怎么优化呢 ...
- 刷题总结———长跑路径(ssoj1982)
题目: 给定一个无向图···求特定几个点中两两间的最短路中的最小值····其中1≤N,M≤100000:T≤5:1≤K≤n:1≤边长≤100000,T为一个测试点的测试数··k为测试点数量 题解: 我 ...
- 学习orm框架及一些看法
首先说说我对现在主流的ORM框架的一些看法: 优点: 让程序员不再关注数据库细节,专心在业务逻辑上,程序员可以不懂数据库就可以开发系统. 让数据库迁移变的非常方便,如果系统需要更改使用的数据库,直接改 ...