ArrayList,从类名就可以看出来,这是由数组实现的List,即内部是用数组保存元素的有序集合。先看看主要的成员变量,比较简单:

public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
/**
* 默认的初始数组大小
*/
private static final int DEFAULT_CAPACITY = 10;/**
*用于储存具体元素的数组对象
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
*
*数组中元素的数量
*/
private int size;

三个构造函数:

/**
*
* 创建一个确定初始大小的数组
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
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);
}
} /**
* 不创建数组,使用默认的空数组
*/
   private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}
    public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
} /**
*传入一个Collection 的实现类 并转换成Object[]数组*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)->有些集合的toArray方法返回的对象类型可能不是Object[].class
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// 使用空数组作为初始数组
this.elementData = EMPTY_ELEMENTDATA;
}
}

如果可以大体上确定ArrayList需要所元素的数量,尽量使用ArrayList(int initialCapacity)指定初始容量,可以有效避免其扩容次数(多数底层使用数组实现的集合有相同的特点)

接下来分析其扩容机制,先看看add(E e)方法:    

public boolean add(E e) {
ensureCapacityInternal(size + 1); //判断是否需要扩容
elementData[size++] = e;//添加对象引用
return true;
} private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
} private static int calculateCapacity(Object[] elementData, int minCapacity) {
//即使用的是无参构造器创建对象,且是第一次添加元素的情况
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//如果当前所需最小容量小于默认值10,则最小值作为当前容量
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
} private void ensureExplicitCapacity(int minCapacity) {
modCount++; //如果当前所需的容量超出,之前数组大小 则进行扩容操作
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//核心扩容方法
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);//原来数组的1.5倍
//新数组的大小必须至少为原数组大小的1.5倍,否则直接扩容为1.5倍
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//新的数组长度如大于默认最大的数组长度MAX_ARRAY_SIZE
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();
//所需最小的数组大小如果大于MAX_ARRAY_SIZE,则直接取int类型最大值,反之,取默认最大值MAX_ARRAY_SIZE
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}

所有的添加操作都是需要调用ensureCapacityInternal方法,判断是否需要,并进行扩容操作

这里总结一下:

  ArrayList默认容量是10,如果初始化时一开始指定了容量,或者通过集合作为元素,则容量为指定的大小或参数集合的大小。每次扩容为原来的1.5倍,如果新增后超过这个容量,则容量为新增后所需的最小容量。如果增加0.5倍后的新容量超过限制的容量,则用所需的最小容量与限制的容量进行判断,超过则指定为Integer的最大值,否则指定为限制容量大小。然后通过数组的复制将原数据复制到一个更大(新的容量大小)的数组。

再看看ArrayList中其他动态的方法:

//根据索引移除
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;
}
//根据具体的对象移除
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;
}
//移除所有元素
public void clear() {
modCount++; // clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null; size = 0;
}
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++;
}
 

所有的移除工作都是找到对应的数组下标项,然后置空,具体的对象回收是jvm的工作;进行删除和插入操作时,为了维持数组中元素的连续性,则需要调用System.arraycopy(),重新并排数组元素,时间复杂度是线性的,效率很低,所以ArrayList不适合需要经常对集合进行动态操作的场景。

  

最后看看ArrayList访问元素的方法:

    public E get(int index) {
rangeCheck(index); return elementData(index);
}

很简单,就是利用数组的索引访问,常数时间复杂度,效率很高,所以对于频繁读取的场景,使用ArrayList再好不过了

    

  

JDK集合框架--ArrayList的更多相关文章

  1. JDK(一)JDK集合框架

    JDK中的集合框架分为两大类:Collection和Map.Collection以一组Object的形式保存元素,Map以Key-Value对的形式保存元素. 上图列出的类并不完整,只列举了平时比较常 ...

  2. 集合框架-ArrayList,Vector,Linkedlist

    // ClassCastException 报错,注意,千万要搞清楚类型 * Vector的特有功能: * 1:添加功能 * public void addElement(Object obj) -- ...

  3. Java基础-集合框架-ArrayList源码分析

    一.JDK中ArrayList是如何实现的 1.先看下ArrayList从上而下的层次图: 说明: 从图中可以看出,ArrayList只是最下层的实现类,集合的规则和扩展都是AbstractList. ...

  4. 大数据学习笔记——Java篇之集合框架(ArrayList)

    Java集合框架学习笔记 1. Java集合框架中各接口或子类的继承以及实现关系图: 2. 数组和集合类的区别整理: 数组: 1. 长度是固定的 2. 既可以存放基本数据类型又可以存放引用数据类型 3 ...

  5. JDK集合框架源码分析 - 简单概要

    1.类继承体系 在集合框架的类继承体系中,最顶层有两个接口Collection.Map: Collection 表示一组纯数据 Map 表示一组key-value对 Collection的类继承体系: ...

  6. JAVA基础--JAVA API集合框架(ArrayList、HashSet、HashMap使用)14

    一.集合Collection 1. 集合介绍 变量:表示的内存中的一个空间,只能保存确定类型的单个数据 数组:表示的是内存中的多个连续的空间,这些空间中可以存储多个同类型的数据. 后期继续学习面向对象 ...

  7. JDK集合框架--综述

       接下来的几篇博客总结一下对jdk中常用集合类知识,本篇博客先整体性地介绍一下集合及其主要的api: 从整体上来说,集合分两大类collection和map: 首先来看看Collection: c ...

  8. 集合框架,ArrayList和Vector的区别,让arrayList线程安全的几种方案

    boolean add(E e) 将指定的元素添加到此列表的尾部. void add(int index, E element) 将指定的元素插入此列表中的指定位置. boolean addAll(C ...

  9. 集合框架 ArrayList LinkedList(待续)

    ArrayList中存储的元素的内存空间是连续的, LinkedList内存空间不是连续的 集合对象不能被序列化到硬盘中 http://blog.csdn.net/eson_15/article/de ...

随机推荐

  1. PHP记录商品历史纪录

    /* 记录浏览历史 */ if (!empty($_COOKIE['history'])) { if(stripos($_COOKIE['history'].',',$goods_id.',')=== ...

  2. wsdl2objc定制(一)namespace

    1.问题抛出: 如今还是有非常多人使用 wsdl2objc 来调用webservice,可是有时候会有不开心的事情发生, <soap:Envelope xmlns:soap="http ...

  3. 2016/05/25 empty() 与 isset()的区别

    对于初学php的人来说,empty()和和isset()用法的区别是很难搞清楚的,他们的用法的差别不仔细去琢磨的话确实很难弄清楚. 先说一下他们的共同点: 都可以判定一个变量是否为空: 都返回bool ...

  4. C++不能在栈上申请动态内存,而只能依靠指针

    以下三种情况皆错,都编译不过: int main(int argc, char* argv[]) { int a; int b[a]; } int main(int argc, char* argv[ ...

  5. codeforces 447 A-E div2 补题

    A DZY Loves Hash 水题 #include<iostream> #include<cstdio> #include<cstdlib> #include ...

  6. JSP指令--include指令(静态包含)

    转自:https://blog.csdn.net/chentiefeng521/article/details/51802319 include指令         include指令是文件加载指令, ...

  7. linux简单技巧和怎么样进入root用户

    1.使用shell的Tab键自动补全 Tab在linux命令行输入中可以自动完成.在linux 命令行中使用Tab键会极大提高输入效率2.使用shell的历史记录 shell会记录用户执行命令的历史记 ...

  8. 设计模式-Template Method Pattern

    将generic部份放在abstract base class中的实现的方法中,而将和具体context相关的部份作为abstract base class的虚方法,由derivatives去实现. ...

  9. Ruby  Numeric类

    Numeric类   Numeric ---------> Integer ---------> Fixnum                                       ...

  10. bzoj 1306: [CQOI2009]match循环赛【dfs+剪枝】

    大力剪枝,最后洛谷上还开了o2才过-- 大概这样剪枝: 1.搜索中,一个队当前得分超过要求或者一个队剩下的比赛场数全赢也达不到要求则return: 2.注意到如果平局,最总分的贡献是2,否则是3,所以 ...