JDK源码那些事儿之常用的ArrayList

栏目: Java · 发布时间: 6年前

内容简介:前面已经讲解集合中的HashMap并且也对其中使用的红黑树结构做了对应的说明,这次就来看下简单一些的另一个集合类,也是日常经常使用到的ArrayList,整体来说,算是比较好理解的集合了,一起来看下jdk版本:1.8

前面已经讲解集合中的HashMap并且也对其中使用的红黑树结构做了对应的说明,这次就来看下简单一些的另一个集合类,也是日常经常使用到的ArrayList,整体来说,算是比较好理解的集合了,一起来看下

前言

jdk版本:1.8

类定义

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

JDK源码那些事儿之常用的ArrayList

变量说明

private static final long serialVersionUID = 8683452581122892189L;

    /**
     * 默认的初始化容量
     * 这里和HashMap初始容量不同,默认10
     * 有些面试官可能问,虽然我感觉没必要记这玩意
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 空集合,在构造函数中看说明
     */
    private static final Object[] 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.
     * 
     * 基于数组实现容量大小变化,上边注释也说了第一次添加元素时,将容量扩展到DEFAULT_CAPACITY
     * 更详细的接着往下看
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * 数组长度,即arraylist的长度
     */
    private int size;
    
    /**
     * 最大数组长度限制
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

从上边变量定义也能看出来ArrayList本质上是基于Object[]实现,故方法上的操作都是基于数组来进行

构造方法

从构造方法中能看出:

  • 如果不设置初始化容量或者初始化赋值集合则elementData赋值为空数组而不是默认容量为10的数组
/**
     * 无参构造方法,初始化为默认空数组
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        // 原集合不为空,则进行复制
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            /**
             * 官方bug
             * c.toArray() 返回类型取决于其实际类型
             * 查了下,应该是调用子类的toArray(重写)方法返回具体的类型
             * 自己多想下也明白了,父类保存了子类的数组对象,这里需要调整成Object[]
             * 不明白的自己Google下
             */ 
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // 原集合为空,elementData赋值为空数组
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }
    /**
     * 初始化容量 代码比较简单
     */
    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);
        }
    }

重要方法

add

每次增加元素时会通过ensureCapacityInternal进行容量大小的验证,不满足则进行扩容操作,通过grow方法进行扩容操作,在允许的范围上扩容为原来的1.5倍

/**
     * 增加元素
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    /**
     * 确认容量
     */
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    /**
     * 计算容量
     * elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * 在这里进行了初始化判断
     * 最小容量为10
     */
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
    /**
     * 修改次数记录modCount,容量是否扩容判断
     */
    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;
        // 右移操作扩容为原来的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);
    }
    /**
     * 大容量值处理
     */
    private static int hugeCapacity(int minCapacity) {
        // 溢出抛出异常
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        // 计算超出时取值判断
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
    /**
     * 将element插入index的位置
     */    
    public void add(int index, E element) {
        rangeCheckForAdd(index);
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // native方法实现拷贝
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

addAll

/**
     * 先对集合容量进行检查,记录修改次数,调用arraycopy将旧数组元素拷贝到新数组元素中
     */ 
    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;
    }
    /**
     * 和上边不同之处在于将数组拷贝到新数组index位置,其后元素依次排序
     */    
    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;
    }

clear

/**
     * 清空
     */ 
    public void clear() {
        modCount++;

        // clear to let GC do its work
        // 注释上也写明了原因,置空为了让GC工作,回收空间
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }

contains

/**
     * 判断某个元素是否在集合中
     */ 
    public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }
    /**
     * 返回元素在集合中的首个索引(从小到大)
     * 主要是判空区分
     */     
    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;
    }

get

/**
     * 获取索引为index的元素,先检查索引值,再调用elementData方法
     */
    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }

iterator

/**
     * 返回迭代器 内部类实现
     */
    public Iterator<E> iterator() {
        return new Itr();
    }
    
    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        Itr() {}

        public boolean hasNext() {
            return cursor != size;
        }
        /**
         * 获取索引为cursor的元素,并置cursor = cursor + 1,方便下次调用,lastRet记录当前返回的元素索引
         */
        @SuppressWarnings("unchecked")
        public E next() {
            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];
        }
        /**
         * 移除当前lastRet对应元素,cursor置为lastRet,修改次数修改
         */
        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();
            }
        }
        /**
         * jdk 1.8新增接口,调用accept接口对每个元素执行动作
         */
        @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();
        }
    }

lastIndexOf

/**
     * 返回匹配对象的首个索引(从大到小)
     */
    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;
    }

remove

/**
     * 删除索引为index的元素
     */
    public E remove(int index) {
        rangeCheck(index);
        //修改记录+1
        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            //使用arraycopy重新整理集合
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }
    /**
     * 根据给定的元素删除,这里看源码也能发现,只删除第一个匹配成功的元素即返回
     */
    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;
    }

removeAll

/**
     * 移除所有和参数集合相同的元素
     */
    public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return batchRemove(c, false);
    }
    private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData;
        int r = 0, w = 0;
        boolean modified = false;
        try {
            for (; r < size; r++)
                //将保留的数据写回elementData
                if (c.contains(elementData[r]) == complement)
                    elementData[w++] = elementData[r];
        } finally {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            if (r != size) {
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                w += size - r;
            }
            if (w != size) {
                // clear to let GC do its work
                for (int i = w; i < size; i++)
                    //清理为空的数据
                    elementData[i] = null;
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        return modified;
    }

set

/**
     * 设置索引为index的值为element
     */
    public E set(int index, E element) {
        rangeCheck(index);

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

toArray

/**
     * 将list元素拷贝返回
     */
    public Object[] toArray() {
        return Arrays.copyOf(elementData, 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;
    }

subList

/**
     * 获取子数组,内部类实现,子数组只是引用了原来的数组,因此改变子数组,相当于改变了原来的数组
     * 子数组不再详细说明,ArrayList类相似,只是多了几个成员变量,来限制范围
     * 源码部分自行查看
     */
    public List<E> subList(int fromIndex, int toIndex) {
        subListRangeCheck(fromIndex, toIndex, size);
        return new SubList(this, 0, fromIndex, toIndex);
    }

总结

整体来看ArrayList源码还是比较简单的,从源码部分也能注意到几个点:

  • ArrayList是基于数组实现的集合类
  • Object数组可以存放null
  • 非线程安全,如需并发线程安全类需使用对应的线程安全包装类保证
  • 如已经确定容量大小,可以提前初始化设置好对应容量以减少中间扩容带来的损耗

总的来说,还是相对比较简单了,希望对各位有所帮助,如有错误,欢迎指正,谢谢


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Mathematica Cookbook

Mathematica Cookbook

Sal Mangano / O'Reilly Media / 2009 / GBP 51.99

As the leading software application for symbolic mathematics, Mathematica is standard in many environments that rely on math, such as science, engineering, financial analysis, software development, an......一起来看看 《Mathematica Cookbook》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具