ArrayList集合(JDK1.8)
简述
List是继承于Collection接口,除了Collection通用的方法以外,扩展了部分只属于List的方法。

常用子类
?ArrayList介绍
1.数据结构
其底层的数据结构是数组,数组元素类型为Object类型,即可以存放所有类型数据。
2.源码分析
2.1类的继承关系
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
说明:ArrayList继承AbstractList抽象父类,实现了List接口(规定了List的操作规范)、RandomAccess(可随机访问)、Cloneable(可拷贝)、Serializable(可序列化)。
2.2类的属性
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
// 版本号
private static final long serialVersionUID = 8683452581122892189L;
// 缺省容量
private static final int DEFAULT_CAPACITY = 10;
// 空对象数组
private static final Object[] EMPTY_ELEMENTDATA = {};
// 缺省空对象数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 元素数组
transient Object[] elementData;
// 实际元素大小,默认为0
private int size;
// 最大数组容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
}
2.3类的构造函数
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) { // 初始容量大于0
this.elementData = new Object[initialCapacity]; // 初始化元素数组
} else if (initialCapacity == 0) { // 初始容量为0
this.elementData = EMPTY_ELEMENTDATA; //为空对象数组
} else { // 初始容量小于0,抛出异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; // 无参构造函数,设置元素数组为空
}
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); // 不为Object数组的话就进行复制
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
说明:当传入的是集合时,会将其转成数组进行处理,并且指定elementData数组的大小,不允许初始化大小小于0,否则抛出异常。
2.4核心方法
1.add函数
public boolean add(E e) { // 添加元素
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // 判断元素数组是否为空数组
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); // 取较大值
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
// 结构性修改加1
modCount++;
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length; // 旧容量
int newCapacity = oldCapacity + (oldCapacity >> 1); // 新容量为旧容量的1.5倍
if (newCapacity - minCapacity < 0) // 新容量小于参数指定容量,修改新容量
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0) // 新容量大于最大容量
newCapacity = hugeCapacity(minCapacity); // 指定新容量
// 拷贝扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
说明:在这里可以很清楚的看到扩容容量的计算:int newCapacity = oldCapacity + (oldCapacity >> 1),其中oldCapacity是原来的容量大小,oldCapacity >> 1 为位运算的右移操作,右移一位相当于除以2,所以这句代码就等于int newCapacity = oldCapacity + oldCapacity / 2;即容量扩大为原来的1.5倍。,获取newCapacity后再对newCapacity的大小进行判断,如果仍然小于minCapacity,则直接让newCapacity 等于minCapacity,而不再计算1.5倍的扩容。然后还要再进行一步判断,即判断当前新容量是否超过最大的容量 if (newCapacity - MAX_ARRAY_SIZE > 0),如果超过,则调用hugeCapacity方法,传进去的是minCapacity,即新增元素后需要的最小容量,如果minCapacity大于MAX_ARRAY_SIZE,则返回Integer的最大值。否则返回MAX_ARRAY_SIZE。
注意:数组在进行扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量的增长大约是其原容量的1.5倍,操作代价很高,最好在预先元素个数有多少个的情况下指定其容量。
举个栗子

说明:可以看出定义一个无参构造函数时,容器为一个空的数组,并且此时的elementData的值为0

说明:在调用了add函数之后,elementData的值被设置成初始值10,并且将元素1添加进数组中,并且size值加1
举个栗子

说明:当如果一开始定义好了集合的长度,那么在一开始初始化容器的时候,elementData的值就被设置好了

说明:而如果在后边添加的元素大于原先初始化的大小,那么ArrayList的大小会按照1.5倍的来进行动态扩建。如上,原先elementData的值为6,被扩大1.5后变成9

说明:无参构造函数也是相同的道理,如果添加的元素长度大于默认值10,会进行1.5倍进行动态扩建,即elementData的值变成15
附:size和modCount的区别
size是ArrayList的变量。modCount是ArrayList的父类AbstractList中的变量,默认值为0。
size记录了ArrayList中元素的数量,modCount记录的是关于元素的数目被修改的次数。modCount在ArrayList的普通操作里可能并没有看出多大用处,但是在涉及到fail-fast就主要是依靠它了。
举个栗子

说明:一开始进行add操作之后,size和modCount的值都是相同的,每次执行都进行加1

说明:当进行remove操作后,容器的元素个数发生改变,所以modCount值会进行加1,而size由于元素减少,进行减1

说明:当再次进行add操作后,size和modCount值同时也会加1

说明:进行set操作进行修改容器元素后,由于元素个数并没有发生改变,所以modCount和size的值都不会发生变化
2、set函数
public E set(int index, E element) {
// 检验索引是否合法
rangeCheck(index);
// 旧值
E oldValue = elementData(index);
// 赋新值
elementData[index] = element;
// 返回旧值
return oldValue;
}
说明:设定指定下标索引的元素值。
3、indexOf函数
// 从首开始查找数组里面是否存在指定元素
public int indexOf(Object o) {
if (o == null) { // 查找的元素为空
for (int i = 0; i < size; i++) // 遍历数组,找到第一个为空的元素,返回下标
if (elementData[i]==null)
return i;
} else { // 查找的元素不为空
for (int i = 0; i < size; i++) // 遍历数组,找到第一个和指定元素相等的元素,返回下标
if (o.equals(elementData[i]))
return i;
}
// 没有找到,返回空
return -1;
}
说明:从头开始查找与指定元素相等的元素,注意,是可以查找null元素的,意味着ArrayList中可以存放null元素的。与此函数对应的lastIndexOf,表示从尾部开始查找。
4、get函数
public E get(int index) {
// 检验索引是否合法
rangeCheck(index);
return elementData(index);
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
E elementData(int index) {
return (E) elementData[index];
}
说明:get函数会检查索引值是否合法(只检查是否大于size,而没有检查是否小于0)。在get函数中存在element函数,element函数用于返回具体的元素,返回的值都经过了向下转型(Object -> E)
举个栗子

说明:当index为负值时,程序运行会发生数组越界异常java.lang.ArrayIndexOutOfBoundsException

说明:而当index大于容器中的size值时,则会发生索引越界异常java.lang.IndexOutOfBoundsException
5、remove函数
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);
// 赋值为空,有利于进行GC
elementData[--size] = null;
// 返回旧值
return oldValue;
}
说明:remove函数用户移除指定下标的元素,此时会把指定下标到数组末尾的元素向前移动一个单位,并且会把数组最后一个元素设置为null,这样是为了方便之后将整个数组不被使用时,会被GC,可以作为小的技巧使用。
注明:本人才疏学浅,以上仅为个人日常笔记总结使用
参考资料:【集合框架】JDK1.8源码分析之ArrayList(六)
<<Thinking In Java(中文版第四版>>
ArrayList集合(JDK1.8)的更多相关文章
- Java集合:ArrayList (JDK1.8 源码解读)
ArrayList ArrayList几乎是每个java开发者最常用也是最熟悉的集合,看到ArrayList这个名字就知道,它必然是以数组方式实现的集合 关注点 说一下ArrayList的几个特点,也 ...
- Java 集合 JDK1.7的LinkedList
Java 集合 JDK1.7的LinkedList @author ixenos LinkedList LinkedList是List接口的双向链表实现,JDK1.7以前是双向循环链表,以后是双向非循 ...
- java基础32 List集合下的ArrayList集合
单例集合体系: ---------| collection 单例集合的根接口--------------| List 如果实现了list接口的集合类,具备的特点:有序,可重复 注:集合 ...
- Java 之 ArrayList 集合
一.ArrayList 概述 java.util.ArrayList 是 大小可变的数组 的实现,存储在内的数据称为元素.该类是一个 集合类(容器),可以让我们更便捷的存储和操作对象数据. 该类可以 ...
- ArrayList 集合的几种遍历的方法
ArrayList 集合 也可称作动态数组(长度可变),在新建的时候是没有默认长度的,在新增数据长度小于10的时候,ArrayList 的长度会自动设置为10 //了解更多可以按住Ctrl 再点击你 ...
- List集合_介绍&常用方法和ArrayList集合
List集合 我们掌握了Collection接口的使用后,再来看看Collection接口中的子类,他们都具备那些特性呢? 接下来,我们一起学习Collection中的常用几个子类(java.util ...
- Arraylist集合的概述和基本使用与常用方法
什么是ArrayList类 java.util.ArrayList 是大小可变的数组实现的,存储在内的数据称为元素,此类提供一些方法来操作内部存储的元素.ArrayList中可不断添加元素,其大小也自 ...
- C#语言基础——集合(ArrayList集合)
集合及特殊集合 集合的基本信息: System.Collections 命名空间包含接口和类,这些接口和类定义各种对象(如列表.队列.位数组.哈希表和字典)的集合.System.Collections ...
- ArrayList集合 、特殊集合
一.ArrayList集合 集合内可以放不同类型的元素 另:object类型为所有数据类型的基类 添加元素:.add(); 清空集合:al.clear(); 克隆集合:.clone(); 判断是否包含 ...
- 2016年10月16日--ArrayList集合、特殊集合
ArrayList集合 使用前引用 using System.Collections; ArrayList集合 实例化.初始化 ArrayList al = new ArrayList(); Arra ...
随机推荐
- java 丢失的异常
采用finally从句中的,可能会丢失异常 package thinking; //: LostMessage.java // How an exception can be lost class V ...
- logrotate日志转储
1 工具目录 ***系统开启selinux,logrotate会不生效*** linux默认会安装logrotate工具,自身的boot.log就是通过它分割转储的. [root@webmaster ...
- Github配置SSH连接
安装git.exe,打开Git Bash 1.检查是否已经有SSH Key. $cd /.ssh 2.生成一个新的SSH. $ ssh-keygen -t rsa -C "email@git ...
- 高阶组件(Higher-Order Components)
有时候人们很喜欢造一些名字很吓人的名词,让人一听这个名词就觉得自己不可能学会,从而让人望而却步.但是其实这些名词背后所代表的东西其实很简单. 我不能说高阶组件就是这么一个东西.但是它是一个概念上很简单 ...
- Kotlin学习的一些笔记
Introduction 写在前面 关于本书 这本书适合你吗? 关于作者 介绍 什么是Kotlin? 我们通过Kotlin得到什么 准备工作 Android Studio 安装Kotlin插件 创建一 ...
- PMP项目管理学习笔记(6)——整合管理之制订项目管理计划
制订项目管理计划 输入:项目章程.组织过程资产.企业环境要素.计划过程的输出(): 工具:专家判断 输出:项目管理计划 项目管理计划使你在问题发生之前做出规划 你要在计划过程组中明确如何完成项目——因 ...
- vue同胞组件通讯解决方案(以下为一种另外可用vuex解决)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- 洛谷 P2319 [HNOI2006]超级英雄
题目描述 题目描述 现在电视台有一种节目叫做超级英雄,大概的流程就是每位选手到台上回答主持人的几个问题,然后根据回答问题的多少获得不同数目的奖品或奖金.主持人问题准备了若干道题目,只有当选手正确回答一 ...
- 洛谷 P1163 银行贷款
题目描述 当一个人从银行贷款后,在一段时间内他(她)将不得不每月偿还固定的分期付款.这个问题要求计算出贷款者向银行支付的利率.假设利率按月累计. 输入输出格式 输入格式: 输入文件仅一行包含三个用空格 ...
- MFC技术积累——基于MFC对话框类的那些事儿2
3. 绘图 3.1 对话框资源编辑 首先通过添加控件的方式来创建一个简单的绘图对话框如图所示,创建步骤为: 第一.在VC++6.0软件环境的灰色空白区域右击,选中Controls,然后会弹出一个控件对 ...