源码阅读——走进ArrayList的内心世界

世人都说ArrayList好是一个人见人夸的好姑娘:

她善解人意(可以动态的扩容),思路灵活(元素可随机访问),心思缜密(元素有序且可以重复)。这篇文章让我们来深入的了解她的内心世界。

上一篇总结了阅读源码的方法,这一篇开始阅读ArrayList的源码。同样,参考YSOcean的优质文章JDK1.8源码(五)——java.util.ArrayList 类

本文的目录

  • ArrayList的使用
  • ArrayList的定义
  • ArrayList的字段属性(五个)
  • ArrayList的构造函数(三个)
  • ArrayList的主要功能
    • 删(根据索引删除、直接删除)
    • 查(根据索引查找、根据元素查找)
  • 使用ArrayList的遍历方法
  • ArrayList类的其他方法(trimToSize方法)

ArrayList的使用

在阅读源码之前,我们要知道ArrayList是做什么的,如果精通使用它,那么我们就可以更好的理解它的源码了。所以在最后边,我贴出使用ArrayList的使用,方便理解。

ArrayList的定义

ArrayList 是一个用数组实现的集合,支持随机访问,元素有序且可以重复。(他的特点在于动态扩容)

  • 实现了RandomAccess接口
    这是一个标记接口,一般标记接口用于List实现,以表明它们支持快速(通常是恒定时间)的随机访问。该接口的主要目的是允许通用算法改变其行为,以便在应用于随机或顺序访问列表时提供良好的性能。
  • 实现了Cloneable接口
    Cloneable 和 RandomAccess 接口一样也是一个标记接口,接口内无任何方法体和常量的声明,也就是说如果想克隆对象,必须要实现 Cloneable 接口,表明该类是可以被克隆的。
  • 实现了Serializable接口
    也是标记接口,表示能被序列化。
  • 实现了List接口
    这个接口是 List 类集合的上层接口,定义了实现该接口的类都必须要实现的一组方法

ArrayList的字段属性(五个)

  • 集合的默认大小:private static final int DEFAULT_CAPACITY = 10;
  • 空的数组实例:private static final Object[] EMPTY_ELEMENTDATA = {};
  • 这也是一个空的数组实例,和EMPTY_ELEMENTDATA空数组相比是用于了解数组第一次添加元素时数组膨胀多少:private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
  • 存储 ArrayList集合的元素,集合的长度即这个数组的长度 :transient Object[] elementData;
    • 1、当 elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 时将会清空 ArrayList
    • 2、当添加第一个元素时,elementData 长度会扩展为 DEFAULT_CAPACITY=10
  • 表示集合的长度:private int size;

ArrayList的构造函数(三个)

  • 此无参构造函数将创建一个 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 声明的数组,注意此时初始容量是0,而不是大家以为的 10。
    public ArrayList() {
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        }
  • 初始化集合大小创建 ArrayList 集合。当大于0时,给定多少那就创建多大的数组;当等于0时,创建一个空数组;当小于0时,抛出异常。
    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);
            }
        }
  • 这是将已有的集合复制到 ArrayList 集合中去。
    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;
            }
        }

ArrayList的主要功能(增删查改)

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

    private void ensureCapacityInternal(int minCapacity) {
        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);
    }
  • 源码的代码流程:
    • 添加元素之前,我们要首先调用 ensureCapacityInternal 方法来确定集合的大小,如果集合满了,则要进行扩容操作。
      • (ensureCapacityInternal方法 )调用ensureExplicitCapacity方法
        • (ensureExplicitCapacity方法)首先对修改次数modCount加一
        • 判断minCapacity是否大于当前ArrayList内部数组长度,大于的话调用grow方法对内部数组elementData扩容
        • (grow方法扩容)
    • 添加元素
    • 返回true
  • 实际样例流程
    • ①、当通过 ArrayList() 构造一个空集合,初始长度是为0的,第 1 次添加元素,会创建一个长度为10的数组,并将该元素赋值到数组的第一个位置。
    • ②、第 2 次添加元素,集合不为空,而且由于集合的长度size+1是小于数组的长度10,所以直接添加元素到数组的第二个位置,不用扩容。
    • ③、第 11 次添加元素,此时 size+1 = 11,而数组长度是10,这时候创建一个长度为10+10*0.5 = 15 的数组(扩容1.5倍),然后将原数组元素引用拷贝到新数组。并将第 11 次添加的元素赋值到新数组下标为10的位置。
    • ④、第 Integer.MAX_VALUE - 8 = 2147483639,然后 2147483639%1.5=1431655759(这个数是要进行扩容) 次添加元素,为了防止溢出,此时会直接创建一个 1431655759+1 大小的数组,这样一直,每次添加一个元素,都只扩大一个范围。
    • ⑤、第 Integer.MAX_VALUE - 7 次添加元素时,创建一个大小为 Integer.MAX_VALUE 的数组,在进行元素添加。
    • ⑥、第 Integer.MAX_VALUE + 1 次添加元素时,抛出 OutOfMemoryError 异常。
    • 注意:能向集合中添加 null 的,因为数组可以有 null 值存在。
//ArrayList源码  Add方法
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

    private void ensureCapacityInternal(int minCapacity) {
        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);
    }

     /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    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);
    }

  • 根据索引删除元素
    • 首先判断给定个索引的范围,小于0或超过集合大小会抛出异常
    • 调用快速删除方法,返回true
  • 直接删除指定元素(删除第一次出现的元素,然后使用arraycopy进行数组自身拷贝)
    • 如果为空,则遍历,找到第一个为空的元素,快速删除,返回true
    • 如果不为空,遍历,通过equals方法判断是否相等,找到第一个出现的,快速删除,返回true
    • 如果上边两个都没执行,返回false
  • 前两者都用到了快速删除方法:fastRemove(int index)
    • 计算index之后的数组的长度numMoved
    • 通过arraycopy方法将老数组index+1之后长度为numMoved的元素拷贝到从老数组index开始的位置,并将最后一个元素设置为null便于垃圾回收
//ArrayList源码  remove方法
    /**
     * Removes the element at the specified position in this list.
     * Shifts any subsequent elements to the left (subtracts one from their
     * indices).
     *
     * @param index the index of the element to be removed
     * @return the element that was removed from the list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    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; // clear to let GC do its work

        return oldValue;
    }

    /**
     * Removes the first occurrence of the specified element from this list,
     * if it is present.  If the list does not contain the element, it is
     * unchanged.  More formally, removes the element with the lowest index
     * <tt>i</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>
     * (if such an element exists).  Returns <tt>true</tt> if this list
     * contained the specified element (or equivalently, if this list
     * changed as a result of the call).
     *
     * @param o element to be removed from this list, if present
     * @return <tt>true</tt> if this list contained the specified element
     */
    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 remove method that skips bounds checking and does not
     * return the value removed.
     */
    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
    }

  • 根据索引查找元素
    • 判断索引范围
    • 返回索引的值
  • 根据元素查找索引
    • 如果对象为空,遍历,找到第一个为空的对象,返回索引
    • 如果对象不为空,遍历,找到第一个值为查找的对象的值,返回索引
    • 没有找到,返回-1
    /**
     * Returns the element at the specified position in this list.
     *
     * @param  index index of the element to return
     * @return the element at the specified position in this list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }

    /**
     * Checks if the given index is in range.  If not, throws an appropriate
     * runtime exception.  This method does *not* check if the index is
     * negative: It is always used immediately prior to an array access,
     * which throws an ArrayIndexOutOfBoundsException if index is negative.
     */
    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
 
    /**
     * Returns the index of the first occurrence of the specified element
     * in this list, or -1 if this list does not contain the element.
     * More formally, returns the lowest index <tt>i</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>,
     * or -1 if there is no such index.
     */
    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;
    }

  • 判断索引范围
  • 指定索引 index 处的元素替换为 element
  • 返回原数组的被修改的那个元素
   /**
     * Replaces the element at the specified position in this list with
     * the specified element.
     *
     * @param index index of the element to replace
     * @param element element to be stored at the specified position
     * @return the element previously at the specified position
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E set(int index, E element) {
        rangeCheck(index);

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

使用ArrayList的遍历方法

  • 使用for
  • 迭代器iterator
  • 迭代器的变种 forEach
  • 迭代器 ListIterator
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");

//使用for
for(String str : list){
    System.out.print(str + " ");
}

//使用迭代器
Iterator<String> it = list.iterator();
while(it.hasNext()){
    String str = it.next();
    System.out.print(str+" ");
}

//使用foreach
for(String str : list){
    System.out.print(str + " ");
}

//迭代器 ListIterator
ListIterator<String> listIt = list.listIterator();

//向后遍历
while(listIt.hasNext()){
    System.out.print(listIt.next()+" ");//a b c
}
//向后前遍历,此时由于上面进行了向后遍历,游标已经指向了最后一个元素,所以此处向前遍历能有值
while(listIt.hasPrevious()){
    System.out.print(listIt.previous()+" ");//c b a
}

其他方法

trimToSize方法:该方法用于回收多余的内存。也就是说一旦我们确定集合不在添加多余的元素之后,调用 trimToSize() 方法会将实现集合的数组大小刚好调整为集合元素的大小。注意:该方法会花时间来复制数组元素,所以应该在确定不会添加元素之后在调用。

    /**
     * Trims the capacity of this <tt>ArrayList</tt> instance to be the
     * list's current size.  An application can use this operation to minimize
     * the storage of an <tt>ArrayList</tt> instance.
     */
    public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }
//ArrayList的使用
public static void main(String[] args) {
		// 创建一个空的数组链表对象list,list用来存放String类型的数据
        ArrayList<String> list = new ArrayList<String>();

        // 增加元素到list对象中
        list.add("Item1");
        list.add("Item2");
        list.add(2, "Item3"); // 此条语句将会把“Item3”字符串增加到list的第3个位置。
        list.add("Item4");

        // 显示数组链表中的内容
        System.out.println("The arraylist contains the following elements: "
                + list);

        // 检查元素的位置
        int pos = list.indexOf("Item2");
        System.out.println("The index of Item2 is: " + pos);

        // 检查数组链表是否为空
        boolean check = list.isEmpty();
        System.out.println("Checking if the arraylist is empty: " + check);

        // 获取链表的大小
        int size = list.size();
        System.out.println("The size of the list is: " + size);

        // 检查数组链表中是否包含某元素
        boolean element = list.contains("Item5");
        System.out
                .println("Checking if the arraylist contains the object Item5: "
                        + element);

        // 获取指定位置上的元素
        String item = list.get(0);
        System.out.println("The item is the index 0 is: " + item);

        // 遍历arraylist中的元素

        // 第1种方法: 循环使用元素的索引和链表的大小
        System.out
                .println("Retrieving items with loop using index and size list");
        for (int i = 0; i < list.size(); i++) {
            System.out.println("Index: " + i + " - Item: " + list.get(i));
        }

        // 第2种方法:使用foreach循环
        System.out.println("Retrieving items using foreach loop");
        for (String str : list) {
            System.out.println("Item is: " + str);
        }

        // 第三种方法:使用迭代器
        // hasNext(): 返回true表示链表链表中还有元素
        // next(): 返回下一个元素
        System.out.println("Retrieving items using iterator");
        for (Iterator<String> it = list.iterator(); it.hasNext();) {
            System.out.println("Item is: " + it.next());
        }

        // 替换元素
        list.set(1, "NewItem");
        System.out.println("The arraylist after the replacement is: " + list);

        // 移除元素
        // 移除第0个位置上的元素
        list.remove(0);

        // 移除第一次找到的 "Item3"元素
        list.remove("Item3");

        System.out.println("The final contents of the arraylist are: " + list);

        // 转换 ArrayList 为 Array
        String[] simpleArray = list.toArray(new String[list.size()]);
        System.out.println("The array created after the conversion of our arraylist is: "
                        + Arrays.toString(simpleArray));
    }
发布了52 篇原创文章 · 获赞 36 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/easy_purple/article/details/86673854