JDK源代码学习系列04----ArrayList

                                                                         JDK源代码学习系列04----ArrayList

1.ArrayList简单介绍

ArrayList是基于Object[] 数组的,也就是我们常说的动态数组。它能非常方便的实现数组的添加删除等操作。

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable</span>

ArrayList支持泛型,它继承自AbstractList,实现了List、RandomAccess、Cloneable、java.io.Serializable接口。

List接口定义了列表必须实现的方法。

RandomAccess是一个标记接口,接口内未定义不论什么内容。

实现了Cloneable接口的类,能够调用Object.clone方法返回该对象的浅拷贝。

通过实现 java.io.Serializable 接口以实现序列化功能。

2.ArrayList成员变量

private transient Object[] elementData;//注意keyword transient
private int size;//实际size,不是容量

Javakeyword transient:是为了在序列化时保护对象的某些域不被序列化,在Java序列化Serializable具体解释中有所提及。关于transient的具体内容稍后补上。

3.ArrayList构造函数

 public ArrayList(int initialCapacity) {//为ArrayList初始化容量
	super();
        if (initialCapacity < 0)//若传入參数小于0,则报IllegalArgumentException的错误
            throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
	this.elementData = new Object[initialCapacity];//初始elementData数组的大小为此传入的參数
    }

 public ArrayList() {//ArrayList初始容量,即默认容量为10
	this(10);
    }

 public ArrayList(Collection<? extends E> c) {//把整个集合初始化给ArrayList.注意:该集合内的数据类型必须与ArrayList一致!!!!
	elementData = c.toArray();
	size = elementData.length;
	// c.toArray might (incorrectly) not return Object[] (see 6260652)//!!这是jdk的一个bug,说是c.toArray()不一定返回的是Object类型
	if (elementData.getClass() != Object[].class)
	    elementData = Arrays.copyOf(elementData, size, Object[].class);//调用的Arrays.copyOf()
    }

4.ArrayList的成员函数

a.void trimToSize() 

public void trimToSize() {//节约内存
	modCount++;//此变量记录ArrayList被改变的次数  !!!超级注意!!!
	int oldCapacity = elementData.length;
	if (size < oldCapacity) {//!!size往往不等于elementData.length;elementData.length是数组的初始长度,size是实际内容的长度!!
            elementData = Arrays.copyOf(elementData, size);
	}
    }

modCount变量是记录ArrayList被改变的次数。为什么须要这个变量呢?

ArrayList不是线程安全(异步)的。这里会引出Fail-Fast机制: 

ArrayList不是线程安全的,因此假设在使用迭代器的过程中有其它线程改动了map,那么将抛出ConcurrentModificationException,这就是所谓fail-fast策略。

这一策略在源代码中的实现是通过modCount域,modCount顾名思义就是改动次数,对ArrayList 结构的改动(长度的变化,添加,删除;赋值不是结构变化)都将添加这个值,那么在迭代器初始化过程中会将这个值赋给迭代器的expectedModCount。在迭代过程中,推断modCount跟expectedModCount是否相等,假设不相等就表示已经有其它线程改动了ArrayList。

ArrayList中的mouCount是在他的父类Abstract中申明的。

protected transient int modCount = 0;

b.void ensureCapacity(int minCapacity)

public void ensureCapacity(int minCapacity) {
	modCount++;
	int oldCapacity = elementData.length;
	if (minCapacity > oldCapacity) {
	    Object oldData[] = elementData;
	    int newCapacity = (oldCapacity * 3)/2 + 1;//若传入參数大于原容量,先扩为  1.5*原容量+1
    	    if (newCapacity < minCapacity)
		newCapacity = minCapacity;//若扩为 1.5*原容量+1 后还是小于传入的參数,则把传入的參数作为新容量
            // minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);
	}
    }

ArrayList须要扩容时至少都是扩为 1.5*原容量+1 ,若1.5*原容量+1 还是小于传入的參数,才把传入的參数作为新容量。

c.boolean contains(Object o)

public boolean contains(Object o) {
	return indexOf(o) >= 0;
    }

d.int indexOf(Object o)

 public int indexOf(Object o) {
	if (o == null) {//定位是null也要定位的哦~
	    for (int i = 0; i < size; i++)
		if (elementData[i]==null)
		    return i;
	} else {
	    for (int i = 0; i < size; i++)
		if (o.equals(elementData[i]))
		    return i;
	}
	return -1;
    }

e.Object[] toArray()   /   <T> T[] toArray(T[] a)

调用的是Arrays.copyOf()

public Object[] toArray() {
        return Arrays.copyOf(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());//若传入的数组长度小于size(ArrayList实际长度),则返回一个长度为size新的数组
	System.arraycopy(elementData, 0, a, 0, size);//若传入数组长度相等,则把elementData复制进传入数组
        if (a.length > size)//若传入的a的长度大于原本数组,则后面补null
            a[size] = null;
        return a;
    }

f.E set(int index, E element)

set是直接替换掉该位置元素;而add是插入该位置,其余元素后移。

public E set(int index, E element) {
	RangeCheck(index);//參数检查的方法~~一定要时刻注意參数检查哦~~
	E oldValue = (E) elementData[index];
	elementData[index] = element;
	return oldValue;
    }

g.boolean add(E e)   /   void add(int index, E element)

public boolean add(E e) {
	ensureCapacity(size + 1);  // add()时先扩容!
	elementData[size++] = e;//!!写的非常好,size++的同一时候还完毕了在最后位置的赋值。之所以size++不会报边界溢出的错误是由于上面已经扩容了。
	return true;
    }
public void add(int index, E element) {
	if (index > size || index < 0)throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
	ensureCapacity(size+1);  // Increments modCount!!
	System.arraycopy(elementData, index, elementData, index + 1,size - index);//!!把elementData的数据从index->末尾所有拷贝到从index+1開始,复制长度无size-index
	elementData[index] = element;//相当于把传入的element插入到空出的位置,即原index
	size++;
    }

注意System.arracopy()方法!!

h.E remove(int index)    /    boolean remove(Object o)    /    void fastRemove(int index)

 public E remove(int index) {
	RangeCheck(index);
	modCount++;
	E oldValue = (E) elementData[index];//得到须要返回的被remove掉的元素
	int numMoved = size - index - 1;//复制时复制的长度,
	if (numMoved > 0)
	    System.arraycopy(elementData, index+1, elementData, index,numMoved);//把原数组从index+1-->末尾的数据拷贝到 index的位置,即相当于把原本index上的数据覆盖掉了,这样最后就空出了一个位置。
	elementData[--size] = null; // 先把size减一,在把最后一赋值为null
	return oldValue;
    }
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 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
    }

i.void clear()

 public void clear() {//把每一个都置为null,再设置size=0;
	modCount++;
	// Let gc do its work
	for (int i = 0; i < size; i++)
	    elementData[i] = null;

	size = 0;
    }

g.boolean addAll(int index, Collection<? extends E> c)

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);  // 扩容时传入的參数为:如今的实际长度+传入的集合的长度
	int numMoved = size - index;
	if (numMoved > 0)
	    System.arraycopy(elementData, index, elementData, index + numNew,numMoved);//把原数组从index-->末尾拷贝到 index+numNew-->末尾,即中间空出numNum的长度来为传入的集合做准备。
        System.arraycopy(a, 0, elementData, index, numNew);
	size += numNew;
	return numNew != 0;
    }

5.总结

a.ArrayList动态数组!

b.ArrayList 非线程安全,即 是异步的。 单线程才用ArrayList。

JDK源代码学习系列04----ArrayList

时间: 2024-10-14 09:03:22

JDK源代码学习系列04----ArrayList的相关文章

Java小白集合源码的学习系列:ArrayList

目录 ArrayList源码学习 ArrayList的继承体系 ArrayList核心源码 ArrayList扩容机制 最后的总结 ArrayList源码学习 本文基于JDK1.8版本,对集合中的巨头ArrayList做一定的源码学习,将会参考大量资料,在文章后面都将会给出参考文章链接,本文用以巩固学习知识. ArrayList的继承体系 ArrayList继承了AbstracList这个抽象类,还实现了List接口,提供了添加.删除.修改.遍历等功能.至于其他接口,以后再做总结. ArrayL

Java小白集合源码的学习系列:Vector

目录 Vector源码学习 Vector继承体系 Vector核心源码 基本属性 构造器 扩容机制 Enumeration 概述 源码描述 具体操作 Vector总结 Vector源码学习 前文传送门: Java小白集合源码的学习系列:LinkedList Java小白集合源码的学习系列:ArrayList Vector是JDK1.0中的集合,是集合中的老大哥,其中大部分的方法都被synchronized关键字所修饰,与ArrayList和LinkedList不同,它是线程安全的(关于线程安全,

JDK源码学习系列06----Vector

                                            JDK源码学习系列06----Vector 1.Vector简介 Vector的内部是数组实现的,它和ArrayList非常相似,最大的不同就是 Vector 是线程安全(同步)的. public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.S

JDK源码学习系列07----Stack

                                                               JDK源码学习系列07----Stack 1.Stack源码非常简单 package java.util; public class Stack<E> extends Vector<E> { // 版本ID.这个用于版本升级控制,这里不须理会! private static final long serialVersionUID = 122446316454

JDK源码学习系列08----HashMap

                                                          JDK源码学习系列08----HashMap 1.HashMap简介 HashMap 是一个散列表,它存储的内容是键值对(key-value)映射. HashMap 继承于AbstractMap,实现了Map.Cloneable.java.io.Serializable接口. HashMap 的实现不是同步的,这意味着它不是线程安全的.它的key.value都可以为null.此外,

JDK源码学习系列05----LinkedList

                                         JDK源码学习系列05----LinkedList 1.LinkedList简介 LinkedList是基于双向链表实现的,它也可以被当作堆栈.队列或双端队列进行操作. public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, jav

给jdk写注释系列之jdk1.6容器(1):ArrayList源码解析

原文出自吞噬天地,链接整理自ImportNew 给jdk写注释系列之jdk1.6容器(2):LinkedList源码解析 给jdk写注释系列之jdk1.6容器(3):Iterator设计模式 给jdk写注释系列之jdk1.6容器(4)-HashMap源码解析 给jdk写注释系列之jdk1.6容器(5)-LinkedHashMap源码解析 给jdk写注释系列之jdk1.6容器(6)-HashSet源码解析&Map迭代器 给jdk写注释系列之jdk1.6容器(1):ArrayList源码解析 工作中

深入Java集合学习系列:HashMap的实现原理

参考文献 引用文献:深入Java集合学习系列:HashMap的实现原理,大部分参考这篇博客,只对其中进行稍微修改 自己曾经写过的:Hashmap实现原理 1. HashMap概述: HashMap是基于哈希表的Map接口的非同步实现(Hashtable跟HashMap很像,唯一的区别是Hashtalbe中的方法是线程安全的,也就是同步的).此实现提供所有可选的映射操作,并允许使用null值和null键.此类不保证映射的顺序,特别是它不保证该顺序恒久不变. 2. HashMap的数据结构: 在ja

Java 集合系列 04 LinkedList详细介绍(源码解析)和使用示例

java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java 集合系列 04 LinkedList详细介绍(源码解析)和使用示例 概要  和学习ArrayList一样,接下来呢,我们先对LinkedList有个整体认识,然后再学习它的源码:最后再通过实例来学会使用LinkedList.内容包括:第1部分 LinkedList介绍第2部分 LinkedList数