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. 【Mongodb教程 第十七课 】MongoDB常用命令 数据库命令 集合操作命令

    (1)数据库命令 a)添加用户 db.addUser(‘name’,’pwd’) b)删除用户 db.removeUser(‘name’) c)用户验证 db.auth(‘name’,’pwd’) d ...

  2. udhcp源码详解(一)之文件组织结构(dhcp server) --转

    udhcp目录下有十几个源文件,一个源文件相对应一个模块,完成一系列相关的功能,例如在static_leases.c主要针对static_lease链表增删查找等操作. dhcpd.c——   整个d ...

  3. maven导入dom4j以及jaxen.jar报java.lang.UnsupportedOperationException:错误

    <dependency> <groupId>jaxen</groupId> <artifactId>jaxen</artifactId> & ...

  4. 6 JobApp默认视图开发

    第一步:引入angularjs 添加app模块 现在我们正式进入开发,下面是我们在上一节建立的目录结构: 我们需要再src路径下,新建index.html文件,先引入angularjs文件: < ...

  5. Lucene Core Solr

    Apache Lucene - Welcome to Apache Lucene https://lucene.apache.org/ The Apache LuceneTM project deve ...

  6. Java Socket实战之四:传输压缩对象

    转自:http://developer.51cto.com/art/201202/317546.htm 上一篇文章说到了用Java Socket来传输对象,但是在有些情况下比如网络环境不好或者对象比较 ...

  7. 警告框在asp.net mvc中应用

    Bootstrap与asp.net MVC框架结合,产生警告框.主要是利用控制器的TempData 字典对象 生成临时数据. ASP.NET MVC的TempData用于传输一些临时的数据,例如在各个 ...

  8. (转)C#中数组、ArrayList和List三者的区别

    原文地址:http://blog.csdn.net/zhang_xinxiu/article/details/8657431 在C#中数组,ArrayList,List都能够存储一组对象,那么这三者到 ...

  9. jsp请求转发小例子(转载)

    在服务器端对客户端请求时行转发对其它的对象,如果jsp网页或Servlet 用三个 jsp网页来演示转发: forword1.jsp, 用来提交表单, 将表单内容提交给 forwrod2.jsp,  ...

  10. bzoj 2809: [Apio2012]dispatching【dfs序+主席树】

    可并堆就可以,但是想复健一下主席树. 考虑枚举管理者,然后选忍者的时候在子树中贪心的从小到大选.做成dfs序就是选区间内和小于等于k的最多点.可以用主席树,查询的时候在主席树上二分即可 这里注意,为了 ...