ArrayList源码解析
ArrayList介绍:
ArrayList就是动态数组,用MSDN中的说法,就是Array的复杂版本,它提供了动态的增加和减少元素,实现了ICollection和IList接口,灵活的设置数组的大小等好处。
ArrayList基于List接口实现的大小可变的数组。其实现了所有可选的List操作,并且元素允许为任意类型,包括null元素。除了实现List接口,此类还提供了操作内部用于存储列表数组大小的方法(这个类除了没有实现同步外,功能基本与Vector一致)。
每个ArrayList实例都有一个容量。容量是用于存储列表中元素的数组的大小。它始终至少与列表大小一样大。随着元素添加到ArrayList,其容量会自动增加。除了添加元素具有恒定的摊销时间成本这一事实之外,增长策略并没有详细指出。
我们在添加大容量数据的时候可以使用ensureCapacity方法来主动扩容,这可以减少自动扩容的次数。
注意:
迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法:迭代器的快速失败行为应该仅用于检测 bug。
ArrayList源码主要字段
rivate static final long serialVersionUID = 8683452581122892189L;
//默认初始容量。
private static final int DEFAULT_CAPACITY = 10;
//用于空实例的共享空数组实例。
private static final Object[] EMPTY_ELEMENTDATA = {};
//用于默认大小的空实例的共享空数组实例。
//将其与EMPTY_ELEMENTDATA区分开来,以便知道何时膨胀多少
//添加第一个元素。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//存储ArrayList元素的数组缓冲区。
//ArrayList的容量是这个数组缓冲区的长度。
//任何空ArrayList with elementData ==DEFAULTCAPACITY_EMPTY_ELEMENTDATA
//当添加第一个元素时,*将扩展为DEFAULT_CAPACITY。
transient Object[] elementData; // non-private to simplify nested class access
//ArrayList的大小(包含的元素数量)。
private int size;
构造函数:
(01) ArrayList 实际上是通过一个数组去保存数据的。当我们构造ArrayList时;若使用默认构造函数,则ArrayList的默认容量大小是10。
(02) 当ArrayList容量不足以容纳全部元素时,ArrayList会重新设置容量:新的容量=“(原始容量x3)/2 + 1”。
(03) ArrayList的克隆函数,即是将全部元素克隆到一个数组中
public void TestArrayList(){
//首先创建一个ArrayList
// ArrayList al =new ArrayList();
List al =new ArrayList();
//添加数据
al.add("jj");
al.add("hh");
al.add("ll");
System.out.println(al);
//使用两个参数add方法,将数据添加到对应位置
al.add(0, "gg");
System.out.println(al);
//可以添加不同数据类型进来
// al.add(new User());
}
//初始化容量为 10 的空列表
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
存储和扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
}
//试图尝试创建一个比 Integer.MAX_VALUE - 8 大的数组
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//扩容操作:
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//扩容为当前容量的 1.5 倍
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);
//将指定的元素追加到此列表的末尾
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
删除:
//从列表中删除指定位置的元素,并将其后位置的元素向左移动
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);
//置null,让 GC 可以工作
elementData[--size] = null;
return oldValue;
}
//删除列表中所有元素
public void clear() {
modCount++;
// 置 null 以便让 GC 回收
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
ArrayList的序列化
public class ListDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
List<String>list = new ArrayList<>();
list.add("hello");
list.add("world");
//序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("file"));
oos.writeObject(list);
}
ArrayList3种遍历方式:
第一种,通过迭代器遍历。即通过Iterator去遍历
Integer value = null;
Iterator iter = list.iterator();
while (iter.hasNext()) {
value = (Integer)iter.next();
}
第二种,随机访问,通过索引值去遍历
Integer value = null;
int size = list.size();
for (int i=0; i<size; i++) {
value = (Integer)list.get(i);
}
第三种,for循环遍历。如下:
Integer value = null;
for (Integer integ:list) {
value = integ;
}
ArrayList与Collection关系如下图:
ArrayList包含了两个重要的对象:elementData 和 size。
(01) elementData 是"Object[]类型的数组",它保存了添加到ArrayList中的元素。实际上,elementData是个动态数组,我们能通过构造函数 ArrayList(int initialCapacity)来执行它的初始容量为initialCapacity;如果通过不含参数的构造函数ArrayList()来创建ArrayList,则elementData的容量默认是10。elementData数组的大小会根据ArrayList容量的增长而动态的增长,具体的增长方式,请参考源码分析中的ensureCapacity()函数。
(02) size 则是动态数组的实际大小。
关于ArrayList的实例:
public class TestArrayList {
@Test
public void TestArrayList1(){
//泛型,使用泛型不仅仅是容器更加明确需要存储类型
//也避免其他类型的混淆
List<String> list = new ArrayList<String>(20);
list.add("曹操");
list.add(0, "刘备");
list.add(0, "孙权");
System.out.println(list);
String name = list.get(1);
System.out.println(name);
list.remove("曹操");
System.out.println(list);
}
@Test
public void TestArrayList2(){
//在jdk7中,泛型可以简化书写
//在实例对象上可以直接书写<>,那么泛型就由引用上决定其类型
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(11);
list.add(111);
list.add(1111);
list.add(11111);
System.out.println(list);
//传递的值如果为基本数据,默认是小下标
list.remove(1);
//如果保存的是数值,请使用相应的包装类
list.remove(new Integer(1));
System.out.println(list);
//清楚所有数据
//list.clear();
System.out.println(list);
//contains包含某个元素
System.out.println(list.contains(111));
}
@Test
public void TestArrayList3(){
//在jdk8之前ArrayList<>();ArrayList<>(10);本质是一模一样的
//在jdk8之后,一般建议使用ArrayList<>(10);
List<String> list = new ArrayList<>();
//使用Arrays工具类,快速生成一个列表
List<String> list2 = Arrays.asList("宋江","李逵","林冲","卢俊义","吴用","公孙胜");
//for循环遍历
for(int i = 0;i < list2.size();i++){
System.out.println(list2.get(i));
}
//使用迭代器,迭代对象
Iterator<String> it = list2.iterator();
for(;it.hasNext();){
System.out.println(it.next());
}
}
}
以上这些是我今天为大家介绍的一些ArrayList的源码解析,还有一些代码实例,大家可以进行一些参考,如果我有什么不对的地方欢迎大家纠正并评论。