美文网首页
java.util系列源码解读之ArrayList

java.util系列源码解读之ArrayList

作者: 4762d2980c91 | 来源:发表于2016-09-01 11:55 被阅读66次

    Java中ArrayList的实现原理: ArrayList内部会维护一个Object的数组对象, 针对ArrayList的任何操作在内部都是通过操作Object这个数组对象的实现的,那么ArrayList是如何实现动态的扩容的呢? 首先ArrayList中有一个成员变量size来记录ArrayList实际存储的数据量大小, 当每次add新的数据时,就会通过这个size和Object数组的length比较,当size + 1>length就会扩容到原数组大小的1.5倍,然后再执行add操作.
    效率: 根据索引查找快, 末尾插入快, 中间插入要执行后面位置数据复制, 查找特定对象慢(一样要循环比较)

    成员变量讲解

    修饰符 变量名 作用
    private static final int DEFAULT_CAPACITY 默认的初始化数组存储空间大小
    private static final Object[] EMPTY_ELEMENTDATA 空的实例list
    private transient Object[] elementData 存储数据的数组对象
    private int size list中存储数据的实际容量
    protected transient int modCount 继承自AbstractList,记录size变化的次数

    构造方法

    空的构造方法: 创建一个空的list, 不包含任何数据, list中包含的数组长度也是空的,
    此种方式下,当add第一个元素时,才会用DEFAULT_CAPACITY默认容量来重新new一个Object[]

    public ArrayList() {
        super();
        this.elementData = EMPTY_ELEMENTDATA;
    }
    

    构造初始化数组容量的list: 根据initialCapacity参数来实例化一个存储数据的数组

    public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
    }
    

    从其他Collection构造:

    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        size = elementData.length;
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)  // 类型转换
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    }
    

    方法选讲

    public int indexOf(Object o)
    返回对象o在list中第一次出现的下标值

    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;
    }
    

    public int lastIndexOf(Object o)
    返回对象o在list中最后一次出现的下标值

    public int lastIndexOf(Object o) {
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }
    

    public E get(int index)
    获取下标index处的对象

    public E get(int index) {
        rangeCheck(index);  // 范围检查
        return elementData(index); // elementData[index]
    }
    

    public boolean add(E e)
    在list末尾添加一个新的元素,同时size执行加1操作 此操作会先检查数组的容量(目的:如果当前数组所有位置都已存放了数据,直接添加就会报错,下标越界,所以要先检查容量是否已满)

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // 扩充数组容量, 抛出OutOfMemoryError
        elementData[size++] = e; // 在末尾设置值
        return true;
    }
    

    public void add(int index, E element)
    在指定下标位置添加元素, 同时size+1操作, 此方法跟add()方法一样

    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++;
    }
    

    public boolean retainAll(Collection<?> c)
    从当前list中移除在c中不存在的元素

    ArrayList中的迭代器 Iterator 讲解
    首先我们要清楚的一点就是迭代器的工作原理是什么? list中的迭代器是根据list的修改次数来实现迭代的,什么意思呢?说白一点就是根据list的继承成员属性modCount来工作的.modCount-list的size发生改变modCount就会加一或者减一,这里要注意的是size的改变时针对的size的改变次数,而不是size的变化长度,也就是说如果size一次增加的数量不管是多少,modCount始终只会加一; 比如add()一次增加一个元素modCount+1, addAll()一次增加多个元素modCount还是+1.这样造成的问题有:
    a. 如果我们在用Iterator做循环操作时, 在循环操作中调用了add(), remove()等会改变size值的操作, 都将引起异常抛出(ConcurrentModificationException);
    b. 在多线程的环境下, 如果一个线程用Iterator在操作list,而另一个线程在操作同一个list,那么也会引起抛出异常(ConcurrentModificationException).此种情况可以考虑==Vector==;
    ==补充:在这种情况下使用Vector也是于事无补,虽然在Vector中针对Iterator的next()和remove()都添加了针对当前list的synchronized条件,但始终next()和add()这些并不是一个原子性的操作,所以在Vector中还是会有同样的问题存在.==

    ArrayList中有两种Iterator

    1. Itr内部类-iterator()方法, 可执行hasNext, next(), remove()操作,要remove()则必须先执行next();
    2. ListItr内部类-listIterator()方法,多了操作list数据的方法add(), set()方法

    ArrayList影响modCount的方法有: add(), remove(),clear(),addAll();也就是这些方法都会影响Iterator迭代器的执行.

    相关文章

      网友评论

          本文标题:java.util系列源码解读之ArrayList

          本文链接:https://www.haomeiwen.com/subject/vcicettx.html