首页 > 其他 > 详细

ArrayList源代码总结,基于JDK8

时间:2020-03-22 00:19:58      阅读:69      评论:0      收藏:0      [点我收藏+]

ArrayList源代码,基于JDK8

使用动态数组实现,所谓动态数组,其实就是数组满了就扩容。

可以存null值

ArrayList内部是通过数组进行实现的,具有高效的随机访问的特性;但插入和删除元素时往往需要复制数组,开销较大。在容器创建完成后需要进行大量访问,但插入和删除操作使用较少的情况下比较适合使用ArrayList。

ArrayList的继承与实现

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

继承的AbstractList中有一个参数modCount
protected transient int modCount = 0;

使用 modCount 记录列表发生结构化修改的次数,从而提供 fail-fast 的迭代器。因为 ArrayList 的实现是非同步的,如果在迭代过程中另一个线程向同一个容器中添加元素或移除元素,就会导致ConcurrentModificationExceptions。

个人理解:modCount类似于乐观锁中的version机制,进行冲突检测。每当对该list增加元素或者删除元素一次,modCount++。

成员变量

private static final long serialVersionUID = 8683452581122892189L;

/**
 * Default initial capacity.
 * 初始数组大小,默认为10
 */
private static final int DEFAULT_CAPACITY = 10;

/**
 * Shared empty array instance used for empty instances.
 * 当自己安排了数组大小后,同时初始是一个空数组,就用这个赋空
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

/**
 * Shared empty array instance used for default sized empty instances. We
 * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
 * first element is added.
 * jdk8的EMPTY_ELEMENTDATA在一定程度上减少了空数组的存在,降低内存的消耗,一定程度上是对性能的优化
 * 当使用默认的数组大小的时候,同时初始是空数组,使用这个来赋予空
 */
 
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/**
 * The array buffer into which the elements of the ArrayList are stored.
 * The capacity of the ArrayList is the length of this array buffer. Any
 * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
 * will be expanded to DEFAULT_CAPACITY when the first element is added.
 * 存储数组
 */
transient Object[] elementData; // non-private to simplify nested class access

/**
 * The size of the ArrayList (the number of elements it contains).
 * 数组的大小
 * @serial
 * 
 */
private int size;

值得注意的是存储对象的数组,使用 transient 来修饰

transient 是一个关键字,用 transient 修饰的变量不再是对象持久化的一部分,即默认序列化机制中该变量不用被序列化。

如果不用被序列化,那么反序列化的时候不是就丢失了存储的数据了吗?

ArrayList 中对序列化和反序列化过程进行了更细致的控制,即通过 writeObject() 和 readObject() 方法。

private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    elementData = EMPTY_ELEMENTDATA;

    // Read in size, and any hidden stuff
    s.defaultReadObject();

    // Read in capacity
    s.readInt(); // ignored

    if (size > 0) {
        // be like clone(), allocate array based upon size not capacity
        int capacity = calculateCapacity(elementData, size);
        SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
        ensureCapacityInternal(size);

        Object[] a = elementData;
        // Read in all elements in the proper order.
        for (int i=0; i<size; i++) {
            a[i] = s.readObject();
        }
    }
}

private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException{
    // Write out element count, and any hidden stuff
    int expectedModCount = modCount;
    s.defaultWriteObject();

    // Write out size as capacity for behavioural compatibility with clone()
    s.writeInt(size);

    // Write out all elements in the proper order.
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }

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

构造函数

无参数构造函数

/**
 * Constructs an empty list with an initial capacity of ten.
 * 初始为一个空数组,使用初始数组容量10,使用DEFAULTCAPACITY_EMPTY_ELEMENTDATA来为数组赋予空
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

指定数组初始大小的构造函数

/**
 * 指定了数组的初始容量。初始容量指定为0,那么就使用EMPTY_ELEMENTDATA来赋予该list相应的数组为空        
 */
public ArrayList(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);
    }
}

传入一个list的构造函数

生成一个newList,里边的值都是参数list的数据。

是用的浅复制。newList中的对象的内容,发生了变化,那么list中相应的对象内容也会发生变化。比如:newList.get(0)对象调整了成员变量,list.get(0)中的相应成员变量也会改变。具体见Arrays源代码中的Arrays.copyOf()部分

复制的过程只是,根据参数list中的数组,通过Arrays.copyOf生成一个数组给当前list的数组成员变量。

ArrayList<String> newList = new ArrayList<>(list)

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

list.toArray()方法

public Object[] toArray() {
    return Arrays.copyOf(elementData, size);
}

平时我们将list转换为数组,都会使用,有参数的toArray()

String[] strs = list.toArray(new String[list.size()]);

@SuppressWarnings("unchecked")
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;
}

部分成员方法

boolean contains(Object c)

本质上是逐个元素比较 equals,使用参数 c 重写的 equals方法

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

int indexOf(Object 0)

查看被查找对象的索引

内部使用存储对象的equals进行被查找对象的值与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++)
            //使用该对象的继承的类的equals方法
            //如果list存放的是String类型的数据
            //根据多态,参数 Object o = new String("asd")
            //调用equals调用的是子类的重写方法。
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

E get(int index)

public E get(int index) {
    rangeCheck(index);
    
    return elementData(index);
}

判断是否存在越界问题

private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

得到数组中对应的值

@SuppressWarnings("unchecked")
E elementData(int index) {
    return (E) elementData[index];
}

E set(int index,E element)

修改指定索引的值,返回该索引修改之前的值

public E set(int index, E element) {
    rangeCheck(index);

    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

boolean add(E e)

public boolean add(E e) {
    //查看是否数组扩容
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

数组扩容

初始数组容量为10

数组大小不够时,就需要扩容。

在add元素的时候,第一行就是判断是否需要扩容ensureCapacityInternal(size + 1);

private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    //目的是,第一次添加数据的时候,执行grow扩容操作,将elementData扩容到10
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return 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
    // elementData.length,得到当前数组的容量大小,并不是数组中数据个数
    int oldCapacity = elementData.length;
    // oldCapacity >> 1 右一位相当于 除2 ;左移动一位,相当于 乘2
    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);
}

初始状态,数组大小为0的时候,执行扩容操作,数组容量扩充到10
之后每次加值,只要size数不超过10。都不扩容
当数组满了以后,需要加值的时候,执行扩容
数组扩容的大小为1.5倍
通过Arrays.copyOf创建一个新的数组,将原来的数组中的值复制到新的数组中。

void add(int index, E element) 在指定索引处加值

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

查看是否索引越界

private void rangeCheckForAdd(int index) {
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

E remove(int index)

删除指定索引的对象,并返回

public E remove(int index) {
    rangeCheck(index);

    modCount++;
    E oldValue = elementData(index);
    //获得需要移动的数组的
    int numMoved = size - index - 1;
    if (numMoved > 0)
        //从自己的index+1位置开始,复制后边的所有部分,从自己的index位置,逐个放进去
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work

    return oldValue;
}

boolean remove(Object o)

删除list中指定的对象,同时拿着o进入数组进行比较,也是==通过equals通过比较值进行判断==,找到相应的index的位置,通过数组复制向前移位,删除数据。

如果list中有多个对象的值与o相同,那么删除第一个

//根据值去找索引,然后通过数组复制,删除一位
public boolean remove(Object o) {
    if (o == 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; // clear to let GC do its work
}

void clear()

把数组中每项引用都变为null。

public void clear() {
    modCount++;
    
    // clear to let GC do its work
    for (int i = 0; i < size; i++)
        elementData[i] = null;

    size = 0;
}

boolean addAll(Collection<? extends E> c)

将参数中的list的内容全部加到自己身上。

list1.addAll(list)

public boolean addAll(Collection<? extends E> c) {
    Object[] a = c.toArray();
    int numNew = a.length;
    //判断是否需要扩容
    ensureCapacityInternal(size + numNew);  // Increments modCount
    //将要放的部分都放到后边
    System.arraycopy(a, 0, elementData, size, numNew);
    size += numNew;
    return numNew != 0;
}

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

从指定索引开始,插入参数中list的全部数据

public boolean addAll(int index, Collection<? extends E> c) {
    rangeCheckForAdd(index);

    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(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;
}

public T[] toArray(T[] a)

返回一个数组

Integer[] is = list.toArray(new String[list.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());
    //将list对应的数组内容,复制到传进来的参数中
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

Iterator iterator()

迭代器

使用了内部类,该内部类实现了Iterator接口

//调用list.iterator()
public Iterator<E> iterator() {
    //返回一个Iterator接口的实现类对象
    //这个实现类是ArrayList的内部类
    return new Itr();
}
/**
 * An optimized version of AbstractList.Itr
 */
//内部类
private class Itr implements Iterator<E> {
    int cursor;       // 迭代器当前迭代位置的索引
    int lastRet = -1; // index of last element returned; -1 if no such
    //将list的版本号,给迭代器,用于在迭代器迭代过程中,进行冲突检测
    //目的就是不允许在迭代的过程中,增加或者删除list中的对象
    int expectedModCount = modCount;

    Itr() {}
    
    //iterator.hasNext() 实质是查看迭代器的索引到没到最后一个来判断是否是否有下一个值
    public boolean hasNext() {
        return cursor != size;
    }
    
    //获得当前迭代索引的对象 E e = iterator.next()
    //迭代索引+1,获取ArrayList的数组中的指定索引de对象
    @SuppressWarnings("unchecked")
    public E next() {
        //判断是否在迭代过程中,进行了对list增加或者删除数据的操作
        checkForComodification();
        int i = cursor;
        if (i >= size)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        //去除下一个索引的对象
        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }

    public void remove() {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

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

    @Override
    @SuppressWarnings("unchecked")
    public void forEachRemaining(Consumer<? super E> consumer) {
        Objects.requireNonNull(consumer);
        final int size = ArrayList.this.size;
        int i = cursor;
        if (i >= size) {
            return;
        }
        final Object[] elementData = ArrayList.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源代码总结,基于JDK8

原文:https://www.cnblogs.com/littleboat/p/12543365.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!