Java容器学习之ArrayList
一、概述
ArrayList是java中十分常用的集合类,继承于AbstractList,并实现了List、RandomAccess、Cloneable和Serializable接口。ArrayList底层是使用数组来实现的,是一个动态的数组队列,它具有以下特点。
- 可以动态扩容、缩容
- ArrayList的操作不是线程安全的
- 允许元素重复,允许元素为空
ArrayList初始默认大小是10,每次扩容时是原大小的1.5倍,如果一开始就知道需要的Lsit长度,可以指定ArrayList的长度,减少扩容操作。
二、源码分析(JDK 1.8)
2.1 ArrayList属性
// 默认的容量
private static final int DEFAULT_CAPACITY = 10;
// 保存ArrayList中数据使用的数组
transient Object[] elementData
// ArrayList的元素个数
private int size;
此外还有一个成员变量modCount,这个变量用来记录修改次数,增删改的都会改变modCount的值。ArrayList是线程不安全的,如果在使用迭代器的过程中有其他线程修改了List,那么将抛出ConcurrentModificationException,是Fail-Fast策略的应用。
2.2 构造函数
// 构造一个初始容量为10的空列表
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);
}
} // 构造一个包含指定容器元素的列表,按照容器迭代器返回的顺序排列
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有三个构造函数,如上代码所示。
2.3 trimToSize()
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
trimToSize()方法可以将ArrayList的底层数组缩容到当前ArrayList中元素的个数(ArrayList的size值),使用这个方法可以最小化ArrayList使用的存储空间。
2.4 添加
// 在列表尾部添加元素
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++;
}
// 将集合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;
}
add方法中都用到了ensureCapacityInternal方法,这个方法用于对ArrayList进行扩容。
2.5 扩容
// 扩容操作
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
} ensureExplicitCapacity(minCapacity);
} private void ensureExplicitCapacity(int minCapacity) {
modCount++; // overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
// 增加容量,使列表可以的容量至少满足minCapacity个元素
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
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);
}
ensureExplicitCapacity()方法先判断当前数组的容量是否可以满足需求,如果不满足就使用grow进行扩容操作,每次扩容为原来的1.5倍。
2.6 删除
// 删除指定位置的元素,并返回被删除的元素
public E remove(int index) {
// 范围检查
rangeCheck(index); modCount++;
E oldValue = elementData(index);
// 需要移动的元素数
int numMoved = size - index - 1;
if (numMoved > 0)
// 等于0表示删除的是末尾的元素,不需要移动
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work return oldValue;
}
E remove(int index) 删除指定位置的元素
// 范围检查
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
删除前进行边界检查
// 删除列表中首次出现的指定元素,如果有多个只删除第一个
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;
}
// 私有的跳过边界检查快速删除指定位置元素的方法
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
}
public boolean remove(Object o) 删除首次出现的指定元素,若有多个只删除第一个
// 删除指定范围的元素
protected void removeRange(int fromIndex, int toIndex) {
modCount++;
int numMoved = size - toIndex;
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved); // clear to let GC do its work
int newSize = size - (toIndex-fromIndex);
for (int i = newSize; i < size; i++) {
elementData[i] = null;
}
size = newSize;
}
void removeRange(int fromIndex, int toIndex) 删除[fromIndex, toIndex)范围的元素
// 从列表中删除指定集合c中的元素
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
// 列表中保留指定集合c中的元素
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}
// 从List的底层数组中删除或者保留指定集合中元素的值,complement为true是保留指定集合元素,为false是删除指定集合元素
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;
}
// w==size表示全部元素保留,没有删除操作发生,否则返回true,并更改数组
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;
}
在try块中首先遍历数组,如果要保留集合c中的元素就把相同元素移到数组前段,如果要删除集合c中的数据就把不同元素移到数组前段,将剩下的数据保存在0到w之间,
第一个if语句是为了处理c.contains()抛出异常时执行;第二个if语句是处理w之后的空间。
Java容器学习之ArrayList的更多相关文章
- Java容器学习——List
Java容器学习--List 基础知识 数组: 优点:随机存取,可以快速访问元素 缺点:静态分配内存,存在空间闲置或者溢出现象:不适合进行插入和删除操作,需要移动大量元素. 链表: 优点: ...
- java容器学习
容器是java中重要的一部分,其接口的结构如下 Collection | ------------------ Map | | | Set List HashMap | HashSet 顾名思义,容器 ...
- Java API学习(一) ArrayList源码学习
ArrayList在平常用的还挺多的,用起来十分舒服,顺手.这里来学习一下它的源码. 类定义 下面是类的定义: public class ArrayList<E> extends Abst ...
- java容器的数据结构-ArrayList,LinkList,HashMap
ArrayList: 初始容量为10,底层实现是一个数组,Object[] elementData 自动扩容机制,当添加一个元素时,数组长度超过了elementData.leng,则会按照1.5倍进行 ...
- java—容器学习笔记
一:迭代器 刚开始学容器,做了个简单的练习题.. import java.util.ArrayList; import java.util.Collection; import java.util.I ...
- java容器学习笔记
容器 容器的组成 容器有两个接口Map和Collection. collection接口有List类和set类. List类可以分为:Vector.LinkedList.ArrayList.CopyO ...
- Java容器学习之List
List接口继承了Collcetion接口,Collection接口又继承了超级接口Iterable,List是有序列表,实现类有ArrayList.LinkedList.Vector.Stack等. ...
- Java集合类学习-LinkedList, ArrayList, Stack, Queue, Vector
Collection List 在Collection的基础上引入了有序的概念,位置精确:允许相同元素.在列表上迭代通常优于索引遍历.特殊的ListIterator迭代器允许元素插入.替换,双向访问, ...
- Java基础学习笔记六 Java基础语法之类和ArrayList
引用数据类型 引用数据类型分类,提到引用数据类型(类),其实我们对它并不陌生,如使用过的Scanner类.Random类.我们可以把类的类型为两种: 第一种,Java为我们提供好的类,如Scanner ...
随机推荐
- 超简单集成HMS Scan Kit扫码SDK,轻松实现扫码购
前言 在前面的文章中,我们向大家介绍了HMS Scan Kit 的快速集成方法以及HMS Scan Kit和其他开源扫码工具的竞争力对比分析,如果没有看到也没关系,文章下方的往期链接中有文章入口. ...
- Syntax error, insert "}" to complete MethodBody
jsp中代码在Eclipse中打开正常,导入项目导入MyEclipse后显示如下异常: Syntax error, insert "}" to complete MethodBod ...
- rhel7 rpmbuild 制作二进制程序安装包(.rpm) 简单示例
下载rpm-build: # yum install rpm-build 如果上述方式无法安装(没配置网络源,虚拟机下是安装媒介源) 可以用下列方式下载后再安装(实践结果可能版本问题引起的缺少太多的* ...
- CSS粘性定位
粘性定位(position:sticky) 1.定义 粘性定位可以被认为是相对定位和固定定位的混合.元素在跨越特定阈值前为相对定位,之后为固定定位.(MDN传送门) 这个特定阈值指的是 top, ri ...
- POI2014 FAR-FarmCraft
[Farm Craft] [题目描述] mhy住在一棵有n个点的树的1号结点上,每个结点上都有一个妹子. mhy从自己家出发,去给每一个妹子都送一台电脑,每个妹子拿到电脑后就会开始安装zhx牌杀毒软件 ...
- 【区间DP】低价回文
[区间DP]低价回文 标签(空格分隔): 区间DP 回文词 [题目描述] 追踪每头奶牛的去向是一件棘手的任务,为此农夫约翰安装了一套自动系统.他在每头牛身上安装了一个电子身份标签,当奶牛通过扫描器的时 ...
- [SpringBoot]SpringBoot中使用redis事务
本文基于SpringBoot 2.X 事务在关系型数据库的开发中经常用到,其实非关系型数据库,比如redis也有对事务的支持,本文主要探讨在SpringBoot中如何使用redis事务. 事务的相关介 ...
- 使用Splunk监控SAP Dump
最近在尝试使用Splunk对SAP系统进行监控,以Dump监控为例,总结了一点相关信息,记录在这里. 本文链接:https://www.cnblogs.com/hhelibeb/p/13260385. ...
- MCU 51-3定时器
51定时/计数器简介 51单片机有2个16位定时器/计数器:定时器0(T0为P3.4)和定时器1(T1为P3.5).这里所说的16位是指定时/计数器内部分别有16位的计数寄存器. 当工作在定时模式时, ...
- CMDB02/ 单例模式、资产采集参考命令、日志处理
CMDB02/单例模式.资产采集参考命令.日志处理 目录 CMDB02/单例模式.资产采集参考命令.日志处理 1. 单例模式 1.1 多例模式 1.2 单例模式 1.2.1 单例模式/错误 1.2.2 ...