ArrayList源码分析(JDK1.8)

不积跬步,无以至千里;不积小流,无以成江海。从基础做起,一点点积累,加油!

来,首先看一下Java集合类的父子关系(图片转自:http://biancheng.dnbcw.info/1000wen/359774.html):

ArrayList的定义

public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable
  • 从ArrayList<\E>可以看出它是支持泛型的,它继承自AbstractList,实现了List、RandomAccess、Cloneable、java.io.Serializable接口。

Collection接口的方法:

List

定义

public interface List<E> extends Collection<E>

List接口方法:

AbstractList提供了List接口的默认实现(个别方法为抽象方法(get方法))。

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>
//ArrayList中使用的modCount就是AbstractList中声明的
protected transient int modCount = 0;

AbstractList中对于List集合的判断equals()方法

public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof List))
            return false;

        ListIterator<E> e1 = listIterator();
        ListIterator<?> e2 = ((List<?>) o).listIterator();
        while (e1.hasNext() && e2.hasNext()) {
            E o1 = e1.next();
            Object o2 = e2.next();
            if (!(o1==null ? o2==null : o1.equals(o2)))
                return false;
        }
        return !(e1.hasNext() || e2.hasNext());
    }

这个方法我觉得写得特别好,放在这里留着。。。

  • AbstractList继承了AbstractCollection类,实现了List集合的除了get方法之外的所有方法:

  • RandomAccess是一个标记接口,接口内没有定义任何内容。
  • 实现了Cloneable接口的类,可以调用Object.clone方法返回该对象的浅拷贝。
  • 通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。序列化接口没有方法或字段,仅用于标识可序列化的语义。

ArrayList的数据域

    transient Object[] elementData; // non-private to simplify nested class access
    /**
     * The size of the ArrayList (the number of elements it contains).
     */
    private int size;

transient 关键字

  • Java的Serializable提供了一种序列化对象实例的机制。当序列化对象时,可能有一个特殊的对象数据成员,我们不想用Serializable机制来保存它。为了在一个特定对象的一个域上关闭Serializable,可以在这个域前加上关键字transient。
  • transient是Java语言的关键字,用来表示一个域不是该对象序列化的一部分。当一个对象被序列化的时候,transient型变量的值不包括在序列化的表示中,然而非transient型的变量是被包括进去的。

看个列子:

public class Person implements Serializable{

    private static final long serialVersionUID = -3319659493471806232L;
    private String name;
    private transient String pass;

    public Person(String name, String pass) {
        this.name = name;
        this.pass = pass;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", pass=" + pass + "]";
    }
}

public class TestTransient {
    public static void main(String[] args) {
        Person person = new Person("Tome", "1234");
        System.out.println(person);
        try {
            // 序列化,被设置为transient的属性没有被序列化
            ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream(
                    "person.out"));
            o.writeObject(person);
            o.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            // 重新读取内容
            ObjectInputStream in = new ObjectInputStream(new FileInputStream(
                    "person.out"));
            Person readPerson = (Person) in.readObject();
            // 读取后psw的内容为null
            System.out.println(readPerson.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
/**运行结果
*Person [name=Tome, pass=1234]
*Person [name=Tome, pass=null]
*/

被标记为transient的属性在对象被序列化的时候不会被保存。

构造方法分析

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
private static final Object[] EMPTY_ELEMENTDATA = {};

public MyArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

public MyArrayList(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);
    }
}

public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

三个构造方法:

  • 无参构造方法

    • 使用无参构造方法创建的是一个length为0的内部数组,当第一次插入数据时,会对其扩容,扩容为默认的大小10
  • 带有初始容量的构造方法
    • 创建一个默认容量的数组
  • 带有初始集合的构造方法

add(E e)方法

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // 检查是否需要扩容
    elementData[size++] = e;
    return true;
}

private void ensureCapacityInternal(int minCapacity) {
    //如果创建ArrayList时用的是无参构造器,则第一次插入时会进行一次扩容并且扩到默认数组大小10
    //然而如果是使用带有容量参数的构造器且initialCapacity=0时,则不会进行这一步,直接扩展数组容量为minCapacity
    //这就导致一个结果,扩容之后带参数的构造器的数组length为1,而默认的为10
    //所以,带容量参数的初始化时注意自己需要的大小
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    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);
}

容量的拓展将导致数组元素的复制,多次拓展容量将执行多次整个数组内容的复制。若提前能大致判断list的长度,调用ensureCapacity调整容量,将有效的提高运行速度。

可以理解提前分配好空间可以提高运行速度,但是测试发现提高的并不是很大,而且若list原本数据量就不会很大效果将更不明显。

add(int index, E element)

    public void add(int index, E element) {
        rangeCheckForAdd(index);//检查index是否越界

        ensureCapacityInternal(size + 1);  // 确保容量
        System.arraycopy(elementData, index, elementData, index + 1,//将index以后的元素后移一个
                         size - index);
        elementData[index] = element;
        size++;
    }
    private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)//元素可以添加在最后尾端
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    //本地的C/C++库方法,直接操纵内存,速度更快
    public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

indexOf(Object o)

public int indexOf(Object o) {
    if (o == 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;
}

remove(int index)

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; // 将对象的引用置为null,让垃圾回收器可以回收

    return oldValue;
}
//删除等其他操作和插入不同,插入可以将元素插在最后面,而删除等操作是在原有的元素上做修改,所以范围检查不一样
private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

clear()

public void clear() {
    modCount++;

    // 清空让垃圾回收器清理
    for (int i = 0; i < size; i++)
        elementData[i] = null;

    size = 0;
}

clone()

返回此 ArrayList 实例的浅表副本。(不复制这些元素本身。)

    public Object clone() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn‘t happen, since we are Cloneable
            throw new InternalError(e);
        }
    }

调用父类的clone方法返回一个对象的副本,将返回对象的elementData数组的内容赋值为原对象elementData数组的内容,将副本的modCount设置为0。

removeRange(int fromIndex, int toIndex)

    protected void removeRange(int fromIndex, int toIndex) {
        modCount++;
        int numMoved = size - toIndex;
        System.arraycopy(elementData, toIndex, elementData, fromIndex,
                         numMoved);

        // clear to let GC do its work
        int newSize = size - (toIndex-fromIndex);
        for (int i = newSize; i < size; i++) {
            elementData[i] = null;
        }
        size = newSize;
    }

执行过程是将elementData从toIndex位置开始的元素向前移动到fromIndex,然后将toIndex位置之后的元素全部置空顺便修改size。

        ArrayList<Person> arrayList = new ArrayList<Person>();
        arrayList.add(new Person(2));
        arrayList.add(new Person(3));
        arrayList.add(new Person(4));
        arrayList.add(new Person(5));
        arrayList.add(new Person(6));
        arrayList.subList(2, 4).clear();

上面这段代码的运行结果与removeRange(2, 4)一样,原因是:

arrayList.subList(2, 4)创建一个AbstractList的子类SubList的实例,调用clear方法时由于子类SubList没有重写,所以直接调用AbstractList的clear方法,然而,在AbstractList的clear方法中,调用的是removeRange方法,该方法在SubList中重写了;所以,调用的是子类的removeRange,在SubList中有一个指向外部类的指针parent,在removeRange方法中通过parent调用了外部类的removeRange方法。所以经过辗转调用,最终调用的还是外部类的removeRange方法

    public List<E> subList(int fromIndex, int toIndex) {
        subListRangeCheck(fromIndex, toIndex, size);
        return new SubList(this, 0, fromIndex, toIndex);
    }
    //SubList类中没有重写AbstractList的clear方法
    private class SubList extends AbstractList<E> implements RandomAccess {
        private final AbstractList<E> parent;//指向外部类
        private final int parentOffset;
        private final int offset;
        int size;

        SubList(AbstractList<E> parent,
                int offset, int fromIndex, int toIndex) {
            this.parent = parent;
            this.parentOffset = fromIndex;
            this.offset = offset + fromIndex;
            this.size = toIndex - fromIndex;
            this.modCount = ArrayList.this.modCount;
        }
        ...
        protected void removeRange(int fromIndex, int toIndex) {
            checkForComodification();
            parent.removeRange(parentOffset + fromIndex,parentOffset + toIndex);
         //调用外部类的removeRange(),所以最终修改的就是外部类数组的元素,即相当于调用了外部类的removeRange方法
            this.modCount = parent.modCount;
            this.size -= toIndex - fromIndex;
        }
    }
    //AbstractList类中申明的clear()方法
    public void clear() {
        removeRange(0, size());//由于SubList重写了removeRange方法,所以调用SubList的removeRange方法,
    }

每一次对数组做添加元素,删除元素等改变数组大小或者容量的操作时都会引起对modCount的加1, 究竟这个值有什么作用呢?

//返回一个遍历的指针
public Iterator<E> iterator() {
    return new Itr();
}
private class Itr implements Iterator<E> {
    int cursor; // 下一个要返回的元素的下标
    int lastRet = -1; // 最后一个元素的下标
    int expectedModCount = modCount;  //这个的作用就是当用itr来遍历集合时,不允许往里面添加或者删除元素

    public boolean hasNext() {//hasNext方法不会检查expectedModCount 的值,不会引发ConcurrentModificationException异常
        return cursor != size;
    }

    @SuppressWarnings("unchecked")
    public E next() {//next方法在最开始的时候就会使用checkForComodification进行检查
    //所以如果在调用next之前对集合进行了改变则会抛出异常
        checkForComodification();//检查是否改变
        int i = cursor;
        if (i >= size)
            throw new NoSuchElementException();
        Object[] elementData = MyArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }
    //remove方法首先会调用checkForComodification进行检查
    //同时,在MyArrayList.this.remove(lastRet)时会改变modCount,expectedModCount = modCount
    //又让情况变为正常,所以删除之后这两个的值又会想等,在之后调用next的方法时不会炮竹异常
    public void remove() {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
            MyArrayList.this.remove(lastRet);
            cursor = lastRet;
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

    @SuppressWarnings("unchecked")
    public void forEachRemaining(Consumer<? super E> consumer) {
        Objects.requireNonNull(consumer);
        final int size = MyArrayList.this.size;
        int i = cursor;
        if (i >= size) {
            return;
        }
        final Object[] elementData = MyArrayList.this.elementData;
        if (i >= elementData.length) {
            throw new ConcurrentModificationException();
        }
        while (i != size && modCount == expectedModCount) {
            consumer.accept((E) elementData[i++]);
        }
        // update once at end of iteration to reduce heap write traffic
        cursor = i;
        lastRet = i - 1;
        checkForComodification();
    }

    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

ArrayList的三种遍历

  • 使用下标(在遍历的时候可以对集合进行添加删除)
        MyArrayList<Integer> array = new MyArrayList<Integer>();
        array.add(1);
        array.add(2);
        array.add(1, 3);
        array.display();

        for(int i = 0; i < array.size(); i++){
            System.out.println(array.get(i));
            array.remove(new Integer(3));
            if (i == 1) {
                array.add(1);
            }
        }
  • 使用iterator
        Iterator<Integer> it = array.iterator();
        while(it.hasNext()){
            //array.add(4);  会导致modCount发生变化,从而导致next方法的checkModCount抛出异常
            int value = it.next();
            //array.remove(new Integer(value));同样会导致modCount发生变化
            it.remove();//但是可以这样,因为Iterator的remove方法删除元素之后对modCount又进行了复原
            System.out.println(value);
        }
  • 使用forEach循环(其实本质就是利用的Iterator循环)先是it.hasNext(),然后在将value = it.next(),所以执行这段代码时,会先打印第一个值,在进行第二轮循环的value = it.next()时抛出异常
        for(Integer value : array){
            //array.add(4);//发生错误
            //array.remove(value);
            System.out.println(value);
        }

本文参考:http://www.cnblogs.com/hzmark/archive/2012/12/20/ArrayList.html

今天看了大牛的代码很开心,明天再接再厉,加油。感受大牛的风采~~

时间: 2024-11-09 00:30:06

ArrayList源码分析(JDK1.8)的相关文章

ArrayList源码分析--jdk1.8

ArrayList概述   1. ArrayList是可以动态扩容和动态删除冗余容量的索引序列,基于数组实现的集合.  2. ArrayList支持随机访问.克隆.序列化,元素有序且可以重复.  3. ArrayList初始默认长度10,使用Object[]存储各种数据类型. ArrayList数据结构   数据结构是集合的精华所在,数据结构往往也限制了集合的作用和侧重点,了解各种数据结构是我们分析源码的必经之路.  ArrayList的数据结构如下: ArrayList源码分析 /* * 用数

LinkedList源码分析--jdk1.8

JDK1.8 ArrayList源码分析--jdk1.8LinkedList源码分析--jdk1.8 LinkedList概述 ??1.LinkedList是用双向链表实现的集合,基于内部类Node<E>实现的集合.??2.LinkedList支持双向链表访问.克隆.序列化,元素有序且可以重复.??3.LinkedList没有初始化大小,也没有扩容机制,通过头结点.尾节点迭代查找. LinkedList数据结构 ??数据结构是集合的精华所在,数据结构往往也限制了集合的作用和侧重点,了解各种数据

HashMap源码分析--jdk1.8

JDK1.8 ArrayList源码分析--jdk1.8LinkedList源码分析--jdk1.8HashMap源码分析--jdk1.8 HashMap概述 ??1. HashMap是可以动态扩容的数组,基于数组.链表.红黑树实现的集合.??2. HashMap支持键值对取值.克隆.序列化,元素无序,key不可重复value可重复,都可为null.??3. HashMap初始默认长度16,超出扩容2倍,填充因子0.75f.??4.HashMap当链表的长度大于8的且数组大小大于64时,链表结构

AQS源码分析--jdk1.8

JDK1.8 ArrayList源码分析--jdk1.8LinkedList源码分析--jdk1.8HashMap源码分析--jdk1.8AQS源码分析--jdk1.8 AbstractQueuedSynchronizer概述 ??1. AQS是一个基于FIFO队列,可以用于构建锁或者其他相关同步装置的基础框架.??2. AQS提供了双向链表.??3. AQS分为共享模式和独占模式.??4.AQS基于volatile内存可见性和CAS原子性操作实现线程间通信操作. AbstractQueuedS

ReentrantLock源码分析--jdk1.8

JDK1.8 ArrayList源码分析--jdk1.8LinkedList源码分析--jdk1.8HashMap源码分析--jdk1.8AQS源码分析--jdk1.8ReentrantLock源码分析--jdk1.8 ReentrantLock概述 ??1. ReentrantLock是独占锁.??2. ReentrantLock分为公平模式和非公平模式.??3. ReentrantLock锁可重入(重新插入) ReentrantLock源码分析 /** * @since 1.5 * @aut

Java笔记---ArrayList源码分析

一.前言 一直就想看看java的源码,学习一下大牛的编程.这次下狠心花了几个晚上的时间,终于仔细分析了下 ArrayList 的源码(PS:谁说的一个晚上可以看完的?太瞎扯了).现在记录一下所得. 二.ArrayList 源码分析 2.1 如何分析? 想要分析下源码是件好事,但是如何去进行分析呢?以我的例子来说,我进行源码分析的过程如下几步: 找到类:利用 Eclipse 找到所需要分析的类(此处就是 ArrayList) 新建类:新建一个类,命名为 ArrayList,将源码拷贝到该类.因为我

Vector 源码分析 jdk1.8

Vector简介 Vector 是矢量队列,它是JDK1.0版本添加的类.继承于AbstractList,实现了List, RandomAccess, Cloneable这些接口.Vector 继承了AbstractList,实现了List:所以,它是一个队列,支持相关的添加.删除.修改.遍历等功能.Vector 实现了RandmoAccess接口,即提供了随机访问功能.RandmoAccess是java中用来被List实现,为List提供快速访问功能的.在Vector中,我们即可以通过元素的序

Java - ArrayList源码分析

java提高篇(二一)-----ArrayList 一.ArrayList概述 ArrayList是实现List接口的动态数组,所谓动态就是它的大小是可变的.实现了所有可选列表操作,并允许包括 null 在内的所有元素.除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小. 每个ArrayList实例都有一个容量,该容量是指用来存储列表元素的数组的大小.默认初始容量为10.随着ArrayList中元素的增加,它的容量也会不断的自动增长.在每次添加新的元素时,Array

Java集合系列之ArrayList源码分析

一.ArrayList简介 ArrayList是可以动态增长和缩减的索引序列,它是基于数组实现的List类. 该类封装了一个动态再分配的Object[]数组,每一个类对象都有一个capacity属性,表示它们所封装的Object[]数组的长度,当向ArrayList中添加元素时,该属性值会自动增加.如果想ArrayList中添加大量元素,可使用ensureCapacity方法一次性增加capacity,可以减少增加重分配的次数提高性能. ArrayList的用法和Vector向类似,但是Vect

集合类学习之Arraylist 源码分析

1.概述 ArrayList是List接口的可变数组的实现.实现了所有可选列表操作,并允许包括 null 在内的所有元素.除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小. 每个ArrayList实例都有一个容量,该容量是指用来存储列表元素的数组的大小.它总是至少等于列表的大小(如果不指定capacity,默认是10).    /**      * Constructs an empty list with an initial capacity of ten.