Java集合(1):ArrayList
Java容器类的用途是“保存对象”,分为两类:Map——存储“键值对”组成的对象;Collection——存储独立元素。Collection又可以分为List和Set两大块。List保持元素的顺序(有序可重复),而Set不能有重复的元素(无序唯一)。
我们从List中最常用的ArrayList展开对Java集合容器的介绍。
一.ArrayList介绍
ArrayList 是一个数组队列,相当于动态数组。与Java中的数组相比,它的容量能动态增长。它继承于AbstractList,实现了List, RandomAccess, Cloneable, java.io.Serializable这些接口。
1.ArrayList的继承关系
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable{}
2.ArrayList与Collection关系如下图
- ArrayList 继承了AbstractList,实现了List。它是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。
- ArrayList 实现了RandmoAccess接口,即提供了随机访问功能。RandmoAccess是java中用来被List实现,为List提供快速访问功能的。在ArrayList中,我们即可以通过元素的序号快速获取元素对象;这就是快速随机访问。稍后,我们会比较List的“快速随机访问”和“通过Iterator迭代器访问”的效率。
- ArrayList 实现了Cloneable接口,即覆盖了函数clone(),能被克隆。
- ArrayList 实现java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输。
- 和Vector不同,ArrayList中的操作不是线程安全的。所以,建议在单线程中才使用ArrayList,而在多线程中可以选择Vector或者CopyOnWriteArrayList。
二.ArrayList源码解析
1.私有属性
ArrayList定义只定义类两个私有属性:
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer.
*/
private transient Object[] elementData; /**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
elementData存储ArrayList内的元素,size表示它包含的元素的数量。
2.ArrayList构造方法
ArrayList提供了三种方式的构造器,可以构造一个默认初始容量为10的空列表、构造一个指定初始容量的空列表以及构造一个包含指定collection的元素的列表,这些元素按照该collection的迭代器返回它们的顺序排列的。
// ArrayList带容量大小的构造函数。
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
// 新建一个数组
this.elementData = new Object[initialCapacity];
} // ArrayList无参构造函数。默认容量是10。
public ArrayList() {
this(10);
} // 创建一个包含collection的ArrayList
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
3.元素存储
ArrayList提供了set(int index, E element)、add(E e)、add(int index, E element)、addAll(Collection<? extends E> c)、addAll(int index, Collection<? extends E> c)这些添加元素的方法。
// 用指定的元素替代此列表中指定位置上的元素,并返回以前位于该位置上的元素。
public E set(int index, E element) {
RangeCheck(index); E oldValue = (E) elementData[index];
elementData[index] = element;
return oldValue;
}
// 将指定的元素添加到此列表的尾部。
public boolean add(E e) {
ensureCapacity(size + 1);
elementData[size++] = e;
return true;
}
// 将指定的元素插入此列表中的指定位置。
// 如果当前位置有元素,则向右移动当前位于该位置的元素以及所有后续元素(将其索引加1)。
public void add(int index, E element) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
// 如果数组长度不足,将进行扩容。
ensureCapacity(size+1); // Increments modCount!!
// 将 elementData中从Index位置开始、长度为size-index的元素,
// 拷贝到从下标为index+1位置开始的新的elementData数组中。
// 即将当前位于该位置的元素以及所有后续元素右移一个位置。
System.arraycopy(elementData, index, elementData, index + 1, size - index);
elementData[index] = element;
size++;
}
// 按照指定collection的迭代器所返回的元素顺序,将该collection中的所有元素添加到此列表的尾部。
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacity(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
// 从指定的位置开始,将指定collection中的所有元素插入到此列表中。
public boolean addAll(int index, Collection<? extends E> c) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(
"Index: " + index + ", Size: " + size); Object[] a = c.toArray();
int numNew = a.length;
ensureCapacity(size + numNew); // Increments modCount int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew, numMoved); System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
4.元素读取
// 返回此列表中指定位置上的元素。
public E get(int index) {
RangeCheck(index); return (E) elementData[index];
}
5.元素删除
ArrayList提供了根据下标或者指定对象两种方式的删除功能。
(1).remove(int index):
// 移除此列表中指定位置上的元素。
public E remove(int index) {
RangeCheck(index); modCount++;
E oldValue = (E) elementData[index]; int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index, numMoved);
elementData[--size] = null; // Let gc do its work return oldValue;
}
首先是检查范围,修改modCount,保留将要被移除的元素,将移除位置之后的元素向前挪动一个位置,将list末尾元素置空(null),返回被移除的元素。
(2).remove(Object o)
// 移除此列表中首次出现的指定元素(如果存在)。这是应为ArrayList中允许存放重复的元素。
public boolean remove(Object o) {
// 由于ArrayList中允许存放null,因此下面通过两种情况来分别处理。
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
// 类似remove(int index),移除列表中指定位置上的元素。
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; // Let gc do its work
}
首先通过代码可以看到,当移除成功后返回true,否则返回false。remove(Object o)中通过遍历element寻找是否存在传入对象,一旦找到就调用fastRemove移除对象。为什么找到了元素就知道了index,不通过remove(index)来移除元素呢?因为fastRemove跳过了判断边界的处理,因为找到元素就相当于确定了index不会超过边界,而且fastRemove并不返回被移除的元素。fastRemove基本和remove(index)一致。
6.调整数组容量
(1).ensureCapacity
从上面介绍的向ArrayList中存储元素的代码中,我们看到,每当向数组中添加元素时,都要去检查添加后元素的个数是否会超出当前数组的长度,如果超出,数组将会进行扩容,以满足添加数据的需求。数组扩容通过一个公开的方法ensureCapacity(int minCapacity)来实现。在实际添加大量元素前,我也可以使用ensureCapacity来手动增加ArrayList实例的容量,以减少递增式再分配的数量。
public void ensureCapacity(int minCapacity) {
modCount++;
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3)/2 + 1; //增加50%+1
if (newCapacity < minCapacity)
newCapacity = minCapacity;
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
从上述代码中可以看出,数组进行扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量的增长大约是其原容量的1.5倍。这种操作的代价是很高的,因此在实际使用时,我们应该尽量避免数组容量的扩张。当我们可预知要保存的元素的多少时,要在构造ArrayList实例时,就指定其容量,以避免数组扩容的发生。或者根据实际需求,通过调用ensureCapacity方法来手动增加ArrayList实例的容量。
关于ArrayList和Vector区别如下:
- ArrayList在内存不够时默认是扩展50% + 1个,Vector是默认扩展1倍。
- Vector提供indexOf(obj, start)接口,ArrayList没有。
- Vector属于线程安全级别的,但是大多数情况下不使用Vector,因为线程安全需要更大的系统开销。
(2).trimToSize
ArrayList还给我们提供了将底层数组的容量调整为当前列表保存的实际元素的大小的功能。它可以通过trimToSize方法来实现。
public void trimToSize() {
modCount++;
int oldCapacity = elementData.length;
if (size < oldCapacity) {
elementData = Arrays.copyOf(elementData, size);
}
7 }
由于elementData的长度会被拓展,size标记的是其中包含的元素的个数。所以会出现size很小但elementData.length很大的情况,将出现空间的浪费。trimToSize将返回一个新的数组给elementData,元素内容保持不变,length和size相同,节省空间。
7.转为静态数组toArray
注意ArrayList的两个转化为静态数组的toArray方法。
(1). 调用Arrays.copyOf将返回一个数组,数组内容是size个elementData的元素,即拷贝elementData从0至size-1位置的元素到新数组并返回。
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
(2).如果传入数组的长度小于size,返回一个新的数组,大小为size,类型与传入数组相同。所传入数组长度与size相等,则将elementData复制到传入数组中并返回传入的数组。若传入数组长度大于size,除了复制elementData外,还将把返回数组的第size个元素置为空。
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
8.Fail-Fast机制
ArrayList也采用了快速失败的机制,通过记录modCount参数来实现。在面对并发的修改时,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。
快速失败”也就是fail-fast,它是Java集合的一种错误检测机制。当多个线程对集合进行结构上的改变的操作时,有可能会产生fail-fast机制。记住是有可能,而不是一定。例如:假设存在两个线程(线程1、线程2),线程1通过Iterator在遍历集合A中的元素,在某个时候线程2修改了集合A的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出 ConcurrentModificationException 异常,从而产生fail-fast机制。
9.ArrayList遍历方式
ArrayList支持3种遍历方式
1.第一种,通过迭代器遍历。即通过Iterator去遍历。
Integer value = null;
Iterator iter = list.iterator();
while (iter.hasNext()) {
value = (Integer)iter.next();
}
2.第二种,随机访问,通过索引值去遍历。
由于ArrayList实现了RandomAccess接口,它支持通过索引值去随机访问元素。
Integer value = null;
int size = list.size();
for (int i=0; i<size; i++) {
value = (Integer)list.get(i);
}
3.第三种,for循环遍历。
Integer value = null;
for (Integer integ:list) {
value = integ;
}
遍历ArrayList时,使用随机访问(即,通过索引序号访问)效率最高,而使用迭代器的效率最低!
10.ArrayList示例
介绍 ArrayList 的常用API
import java.util.*;
/*
* @desc ArrayList常用API的测试程序
* @author skywang
* @email kuiwu-wang@163.com
*/
public class ArrayListTest {
public static void main(String[] args) { // 创建ArrayList
ArrayList list = new ArrayList();
// 将“”
list.add("1");
list.add("2");
list.add("3");
list.add("4");
// 将下面的元素添加到第1个位置
list.add(0, "5");
// 获取第1个元素
System.out.println("the first element is: "+ list.get(0));
// 删除“3”
list.remove("3");
// 获取ArrayList的大小
System.out.println("Arraylist size=: "+ list.size());
// 判断list中是否包含"3"
System.out.println("ArrayList contains 3 is: "+ list.contains(3));
// 设置第2个元素为10
list.set(1, "10");
// 通过Iterator遍历ArrayList
for(Iterator iter = list.iterator(); iter.hasNext(); ) {
System.out.println("next is: "+ iter.next());
}
// 将ArrayList转换为数组
String[] arr = (String[])list.toArray(new String[0]);
for (String str:arr)
System.out.println("str: "+ str);
// 清空ArrayList
list.clear();
// 判断ArrayList是否为空
System.out.println("ArrayList is empty: "+ list.isEmpty());
}
}
参考:
http://www.cnblogs.com/ITtangtang/p/3948555.html
http://www.jb51.net/article/42764.htm
Java集合(1):ArrayList的更多相关文章
- 从源码看Java集合之ArrayList
Java集合之ArrayList - 吃透增删查改 从源码看初始化以及增删查改,学习ArrayList. 先来看下ArrayList定义的几个属性: private static final int ...
- Java集合关于ArrayList
ArrayList实现源码分析 2016-04-11 17:52 by 淮左, 207 阅读, 0 评论, 收藏, 编辑 本文将以以下几个问题来探讨ArrayList的源码实现1.ArrayList的 ...
- Java集合干货——ArrayList源码分析
ArrayList源码分析 前言 在之前的文章中我们提到过ArrayList,ArrayList可以说是每一个学java的人使用最多最熟练的集合了,但是知其然不知其所以然.关于ArrayList的具体 ...
- java集合之ArrayList,TreeSet和HashMap分析
java集合是一个重点和难点,如果我们刻意记住所有的用法与区别则是不太现实的,之前一直在使用相关的集合类,但是没有仔细研究区别,现在来把平时使用比较频繁的一些集合做一下分析和总结,目的就是以后在需要使 ...
- Java集合:ArrayList的实现原理
Java集合---ArrayList的实现原理 目录: 一. ArrayList概述 二. ArrayList的实现 1) 私有属性 2) 构造方法 3) 元素存储 4) 元素读取 5) 元素删除 ...
- 【源码阅读】Java集合之一 - ArrayList源码深度解读
Java 源码阅读的第一步是Collection框架源码,这也是面试基础中的基础: 针对Collection的源码阅读写一个系列的文章,从ArrayList开始第一篇. ---@pdai JDK版本 ...
- Java集合(六)--ArrayList、LinkedList和Vector对比
在前两篇博客,学习了ArrayList和LinkedList的源码,地址在这: Java集合(五)--LinkedList源码解读 Java集合(四)--基于JDK1.8的ArrayList源码解读 ...
- Java 集合源代码——ArrayList
(1)可以查看大佬们的 详细源码解析 : 连接地址为 : https://blog.csdn.net/zhumingyuan111/article/details/78884746 (2) Array ...
- Java集合:ArrayList (JDK1.8 源码解读)
ArrayList ArrayList几乎是每个java开发者最常用也是最熟悉的集合,看到ArrayList这个名字就知道,它必然是以数组方式实现的集合 关注点 说一下ArrayList的几个特点,也 ...
- java集合-数组ArrayList
1.简介 ArrayList是java集合框架常用的集合类之一,底层是基于数组来实现容量大小动态变化的. 2.类图(JDK 1.8) 下图是ArrayList实现的接口和继承的类关系图: public ...
随机推荐
- Oracle Tuning 总括
oracle tuning 分为3个阶段 1. application 调优阶段, 包括设计的调优, SQL语句调优, 管理权限等内容, (这部分是我的重点) (调优人员 application de ...
- 响应式网页设计:rem、em设置网页字体大小自适应
「rem」是指根元素(root element,html)的字体大小,好开心的是,从遥远的 IE6 到版本帝 Chrome 他们都约好了,根元素默认的 font-size 都是 16px.这样一个新的 ...
- FireFox插件FirePHP调试PHP
如果你和我一样,你会在开发网页项目时候完全无法离开FireBug.这个小巧的"臭虫"是一个神奇而有用的HTML/CSS/JavaScript/Ajax调试器.但是你也许不知道这个还 ...
- 利用多态,实现一般处理程序(ashx)中的AOP(切面编程)
本文是对工作中的项目进行代码优化(完善登陆验证的AOP切面编程)时,所遇到的各种解决方案思考过程. 项目背景:由ashx+nvelocity构建的简单B/S问卷系统,现需要优化登录验证环节(时隔若干个 ...
- Sql Server根据表名生成查询的存储过程(查询条件可选)
static void Main(string[] args) { string 表名 = "water_emstime"; string sql = "exec Get ...
- 用原生JS模仿jquery,需要HTML5的支持
jQuery是现在最流行的JavaScript工具库. 据统计,目前全世界57.3%的网站使用它.也就是说,10个网站里面,有6个使用jQuery.如果只考察使用工具库的网站,这个比例就会上升到惊人的 ...
- Windows防火墙端口规则设置新建方法
from:https://jingyan.baidu.com/article/2a1383289fd094074a134ff0.html Windows防火墙有什么用呢?它是电脑的一道安全屏障,可以有 ...
- 一次显式GC导致的High CPU问题处理过程(转)
项目现场反馈系统出现性能问题,具体表现为:所有的客户端响应极其卡顿. 第一反应推测,难道是DB层面出现阻塞?检查v$session会话状态及等待类型未见异常,应该可以排除DB层面原因导致的可能. 继续 ...
- ddos cc攻击简单介绍(转)
何为syn flood攻击: SYN Flood是一种广为人知的DoS(拒绝服务攻击)是DDoS(分布式拒绝服务攻击)的方式之一,这是一种利用TCP协议缺陷,发送大量伪造的TCP连接请求,从而使得被攻 ...
- Dart基础学习01--走近Dart
什么是Dart 在Dart的官网上是这样介绍Dart的: Dart is an open-source, scalable programming language, with robust libr ...