ArrayList简单学习
类声明:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
AbstractList是个抽象类,RandomAccess是个给List用的标记接口,为了指明这个容器支持快速(一般是常量时间复杂度)的随机访问。
List接口
public interface List<E> extends Collection<E> {
boolean add(E e);
void add(int index, E element);
boolean remove(Object o);
E remove(int index);
E set(int index, E element);
E get(int index);
...
}
ArrayList的类变量
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
private int size;
第一个是默认容量;
第二个是当用户指定ArrayList的容量为0的时候,返回的一个数组。
第三个是一个空数组实例
- 当用户没有指定 ArrayList 的容量时(即调用无参构造函数),返回的是该数组==>刚创建一个 ArrayList 时,其内数据量为 0。
- 当用户第一次添加元素时,该数组将会扩容,变成默认容量为 10(DEFAULT_CAPACITY) 的一个数组===>通过 ensureCapacityInternal() 实现
-它与 EMPTY_ELEMENTDATA 的区别就是:该数组是默认返回的,而后者是在用户指定容量为 0 时返回
第四个是真正保存数据data的数组,ArrayList 的容量就是该数组的长度
第五个显然就是ArrayList里面存了的元素的个数了。
构造函数
无参:
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
就将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);
}
}
就根据这个传进来的容量,new一个相应的Object数组。
如果传进来的是0,就让elementData指向说好的那个代表这个状态的空数组。
add方法之add(E e)
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
介个ensureCapacityInternal方法很明显,就是要确保底层数组的capacity能够装得下原来的size + 1后的数据。
在确保操作完成后,就在末尾添加这个元素。
然后看这个ensureCapacityInternal方法:
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
} ensureExplicitCapacity(minCapacity);
}
第一个if中,意思是这是不是用户调用默认构造器构造了ArrayList实例后第一次添加元素,是的话,此时底层数组的length是为0的。那么就更新这个minCapacity,如果传进来的minCapacity比10小,就变成10.
所以这个方法的意思是,确定正确的底层数组的capacity值。
再调用ensureExplicitCapacity方法:
ensureExplicitCapacity:
private void ensureExplicitCapacity(int minCapacity) {
modCount++; // overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
这个方法意思是,真正去确保底层数组有这个minCapacity的length。
如果现在这个底层数组的length还没到这个需要有的最小capacity的值,就要扩容。
这个modCount++是因为ArrayList的结构要变了,如果扩容的话,底层数组的length变而且添加了一个新值;如果不用扩容,则因为添加了新值,所以结构变化。
然后看这个grow方法:
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);
}
由这个
int newCapacity=oldCapacity+ (oldCapacity >> 1);
可见,默认的扩容方式应该是原来的数组length + 原来的length/2。
如果这个默认的加大方法不足以达到minCapacity的要求,就直接newCapacity为minCapacity,如果newCapacity比最大值还大,就用hugeCapacity方法返回一个符合固定的值(如果很大就返回Integer.MAX_VALUE)。
然后就调用Arrays数组的一个复制数组的方法,elementData指向一个新的容量是newCapacity的新Object数组。(注意还没插入新值的),然后回到add方法里面在已经够容量的数组里面插入新值就搞定。
add(int index, E element)方法
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++;
}
第一行是越界检查的方法。
第二行上面介绍了,是保证底层数组的length足够装下原来的size + 1个数据,调用后可能什么都没做,也可能扩容了。
第三行是调用System的一个static方法,这是个复制数组的方法,把index开始的size - index个元素,移到(同一个数组)的index + 1到index + 1 + (size - index)的位置去,也就是index开始的所有元素往后挪咯。(性能开销就是在这些操作)
然后再在index的位置赋值咯,size即ArrayList容纳的元素个数加一。
remove(int 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;
}
前面两行就不解释了,第三行获得要删除的那个元素。
numMoved——删除这个元素后,index后面的需要往前挪的元素的个数。
numMoved表示在执行删除操作时数组需要移动的元素个数,将elementData数组中从index后一位开始的所有元素(即numMoved个元素)往前"移动"1位(这样一移动,index位置的元素会被后面的元素覆盖,间接起到了删除元素的作用),然后把最后的那个元素置空,(然后gc就可以处理了)同时将size变量减1。
还有个remove(Object o)差不多:
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])) {
fastRemove(index);
return true;
}
}
return false;
} /*
* 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
}
有个删null对象的操作,然后还有个fastRemove而已,不多讲了。
get和set方法
直接用数组的方式get和set,很方便。
总结
- ArrayList底层实现是数组。
- 当使用无参数构造函数创建ArrayList对象时,ArrayList对象中的数组初始长度为0(是一个空数组)。
- ArrayList的扩容策略是每次都增加当前数组长度的一半(非固定分配)。
- ArrayList的扩容方式是直接创建一个新的数组,并将数据拷贝到新数组中。
ArrayList简单学习的更多相关文章
- Log4j简单学习笔记
log4j结构图: 结构图展现出了log4j的主结构.logger:表示记录器,即数据来源:appender:输出源,即输出方式(如:控制台.文件...)layout:输出布局 Logger机滤器:常 ...
- shiro简单学习的简单总结
权限和我有很大渊源. 培训时候的最后一个项目是OA,权限那块却不知如何入手,最后以不是我写的那个模块应付面试. 最开始的是使用session装载用户登录信息,使用简单权限拦截器做到权限控制,利用资源文 ...
- CentOS 简单学习 firewalld的使用
1. centos7 开始 使用firewalld 代替了 iptables 命令工具为 firewall-cmd 帮助信息非常长,简单放到文末 2. 简单使用 首先开启 httpd 一般都自带安装了 ...
- Windows 下 Docker 的简单学习使用过程之一 dockertoolbox
1. Windows 下面运行 Docker 的两个主要工具1): Docker for Windows2): DockerToolbox区别:Docker For Windows 可以理解为是新一代 ...
- 在MVC中实现和网站不同服务器的批量文件下载以及NPOI下载数据到Excel的简单学习
嘿嘿,我来啦,最近忙啦几天,使用MVC把应该实现的一些功能实现了,说起来做项目,实属感觉蛮好的,即可以学习新的东西,又可以增加自己之前知道的知识的巩固,不得不说是双丰收啊,其实这周来就开始面对下载在挣 ...
- Linux——帮助命令简单学习笔记
Linux帮助命令简单学习笔记: 一: 命令名称:man 命令英文原意:manual 命令所在路径:/usr/bin/man 执行权限:所有用户 语法:man [命令或配置文件] 功能描述:获得帮助信 ...
- OI数学 简单学习笔记
基本上只是整理了一下框架,具体的学习给出了个人认为比较好的博客的链接. PART1 数论部分 最大公约数 对于正整数x,y,最大的能同时整除它们的数称为最大公约数 常用的:\(lcm(x,y)=xy\ ...
- mongodb,redis简单学习
2.mongodb安装配置简单学习 配置好数据库路径就可以mongo命令执行交互操作了:先将服务器开起来:在开个cmd执行交互操作 ...
- html css的简单学习(三)
html css的简单学习(三) 前端开发工具:Dreamweaver.Hbuilder.WebStorm.Sublime.PhpStorm...=========================== ...
随机推荐
- BZOJ 1620 [Usaco2008 Nov]Time Management 时间管理:贪心
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1620 题意: 有n个工作,每一个工作完成需要花费的时间为tim[i],完成这项工作的截止日 ...
- hdu-5773 The All-purpose Zero(LIS)
题目链接: The All-purpose Zero Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (J ...
- hdu-4857 逃生(拓扑序)
题目链接: 逃生 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Proble ...
- 004-画图神器-graphviz
1 安装及基本使用 1) 下载安装 下载地址 可以下载安装版进行安装或者解压版直接使用 2) 添加系统path 为了能够在dos中使用命令, 需要添加环境变量 默认安装路径为 C:\Program F ...
- (测试)LaTeX公式
\[a^2+b^2=c^2\] \[F(\omega)=\mathcal{F}[f(t)]=\int_{-\infty}^\infty f(t)e^{-iwt}\,dt\] \[\sum_{i=0}^ ...
- 中缀表达式std
#include<cstdio>#include<cstdlib>#include<string>#include<cstring>using name ...
- HUD1686(KMP入门题)
Oulipo Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Subm ...
- Linux启动eclipse报错找不到java环境解决方法
在Linux mint下,前几天还用得很好的的eclipse,今天开机不知为什么这样. Eclipse 3.6 在 linux mint 12 可以在终端顺利启动Eclipse,但是鼠标双击,或者用起 ...
- UVaLive 3971 Assemble (水题二分+贪心)
题意:你有b元钱,有n个配件,每个配件有各类,品质因子,价格,要每种买一个,让最差的品质因子尽量大. 析:很简单的一个二分题,二分品质因子即可,每次计算要花的钱的多少,每次尽量买便宜且大的品质因子. ...
- [翻译] 正式宣布 .NET 5
原文: Introducing .NET 5 今天,我们宣布 .NET Core 3.0 之后的下一个版本将是 .NET 5 .这将是 .NET 系列的下一个重要版本. 将来只会有一个 .NET ,您 ...