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 ...
随机推荐
- 深入理解JVM(③)Java模块化系统
前言 JDK9引入的Java模块化系统(Java Platform Module System ,JPMS)是 对Java技术的一次重要升级,除了像之前JAR包哪有充当代码的容器之外,还包括: 依赖其 ...
- 【FastDFS】FastDFS 分布式文件系统的安装与使用,看这一篇就够了!!
写在前面 有不少小伙伴在实际工作中,对于如何存储文件(图片.视频.音频等)没有一个很好的解决思路.都明白不能将文件存储在单台服务器的磁盘上,也知道需要将文件进行副本备份.如果自己手动写文件的副本机制, ...
- 02 . 分布式存储之FastDFS 高可用集群部署
单节点部署和原理请看上一篇文章 https://www.cnblogs.com/you-men/p/12863555.html 环境 [Fastdfs-Server] 系统 = CentOS7.3 软 ...
- 二进制小数和IEEE浮点标准
二进制小数 首先复习进位计数制的要素: 数码:用来表示进制数的元素.比如 二进制数的数码为:0,1 十进制数的数码为:0,1,2,3,4,5,6,7,8,9 十六进制数的数码为:0,1,2,3,4,5 ...
- 曹工说面试:当应用依赖jar包的A版本,中间件jar包依赖B版本,两个版本不兼容,这还怎么玩?
背景 大一点的公司,可能有一些组,专门做中间件的:假设,某中间件小组,给你提供了一个jar包,你需要集成到你的应用里.假设,它依赖了一个日期类,版本是v1:我们应用也依赖了同名的一个日期类,版本是v2 ...
- 结合SpEL使用@Value-基于配置文件或非配置的文件的值注入-Spring Boot
本文主要介绍Spring @Value 注解注入属性值的使用方法的分析,文章通过示例代码非常详细地介绍,对于每个人的学习或工作都有一定的参考学习价值 在使用spring框架的项目中,@Value是经常 ...
- Pop!_OS安装与配置(二):基础配置
Pop!_OS相关使用配置 #0x0 书接上回 #0x1 换源 #0x10 nopasswd sudo #0x11 换国内源 0x2 安装应用 #0x0 书接上回 首先请看上一章末尾, Pop!_OS ...
- web 部署专题(七):Ubuntu + Nginx + Flask + Gunicore环境搭建(服务器)
现在我们手里有一个准备发布的项目,那么如何将他上传到你的服务器,并让外网访问呢? 安装: 安装Flask pip3 install flask 安装UWSGI pip3 install uwsgi 安 ...
- .NET Core微服务开发服务间调用篇-GRPC
在单体应用中,相互调用都是在一个进程内部调用,也就是说调用发生在本机内部,因此也被叫做本地方法调用:在微服务中,服务之间调用就变得比较复杂,需要跨网络调用,他们之间的调用相对于与本地方法调用,可称为远 ...
- 安装调试mosquitto日志
1.下载mosquitto安装包https://mosquitto.org/download/2.安装其他依赖包 yum install gcc gcc-c++ libstdc++-devel yum ...