ArrayList 学习笔记

在这里插入图片描述

ArrayList

java中的数组,在创建的时候需要指定数组的长度。一旦创建后,数组的大小就固定了,不能够再变化。
但实际开发过程中,经常需要根据保存对象数量的增加扩大范围。ArrayList 就是数组可调整大小的实现,它允许添加所有元素,也就是说,可以往数组里面添加 null 元素。

properties

先看看所有属性:

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    private static final long serialVersionUID = 8683452581122892189L;

    /**
     * Default initial capacity.
     */
    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.
     */
    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;
 }

可以看到,在JDK8当中,数组的默认初始化容量是10:

private static final int DEFAULT_CAPACITY = 10;

而且它有两个空数组实例。

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

它们都是空数组,区别在于,往里面添加第一个元素的时候,DEFAULTCAPACITY_EMPTY_ELEMENTDATA 会以默认的初始化容量初始化elementData的大小,而EMPTY_ELEMENTDATA 会以给定的容量初始化大小,是这样子吗?我们看看代码。

Constractor ArrayList()
    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

可以看到,初始化的时候,就只是把一个static类型的空数组分给elementData.并没有分配大小。它的大小是0.

    public static void main(String args[]) {
        List list = new ArrayList();
        System.out.println(list.size());
    }

输出内容:

0

Process finished with exit code 0
Constractor ArrayList(int initialCapacity)
    /**
     * Constructs an empty list with the specified initial capacity.
     *
     * @param  initialCapacity  the initial capacity of the list
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     */
    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);
        }
    }

可以看到,一旦给定了初始化容量的大小,就会根据给定的值创建对应长度的Object数组。
可以写一个测试类测试一下。

    public static void main(String args[]) {
        ArrayList arrayList2 = new ArrayList(20);
        System.out.println("ArrayList.size() == " + arrayList2.size());
        arrayList2.add(null);
        arrayList2.add(null);
        arrayList2.add(null);
        arrayList2.add(null);
        System.out.println("ArrayList.size() == " + arrayList2.size());
    }

可以看到输出内容是:

ArrayList.size() == 0
ArrayList.size() == 4

Process finished with exit code 0

用一个给定的值构造ArrayList时。会根据这个值初始化ArrayList的成员变量。 this.elementData = new Object[initialCapacity]; 这个时候它的size没有任何变化,还是0. 但是添加一个元素的时候。add方法会对size进行加一操作。代码: elementData[size++] = e;

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

可以看一下ArrayList的容量扩充机制。比如现在的size是20. 添加一个元素后,他的最小容量是21. 也就是minCapacity的值是21.如果这个时候 elementData是DEFAULTCAPACITY_EMPTY_ELEMENTDATA空数组。就取minCapacity和DEFAULT_CAPACITY的最大值。然后再拿这个值和当前elementData的长度比较,如果确实要增加,新的容量,是原来的容量,向右位移一位后的值加上原来的值。大致上就是以0.5倍的大小进行增长。int newCapacity = oldCapacity + (oldCapacity >> 1); 如果容量超过了最大值:MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; 就取最大的值MAX_ARRAY_SIZE = Integer.MAX_VALUE。MAX_VALUE 的最大值是 0x7fffffff;

    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);
    }
   
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

通过数组下标访问元素

可以看到,根据index获取元素的时候,会先检查index的范围,index必须小于size.否则抛IndexOutOfBoundsException。 然后根据index 直接访问elementData的元素。

    /**
     * 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));
    }
    @SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];
    }

equals(Object o)方法

判断逻辑是:

  1. 先判断是否是同一个对象 o == this. 如果是true,就返回true.
  2. 判断给的对象是不是 List 的实例,如果不是,就返回false.
  3. 获取 this 和给定对象的迭代器 listIterator(). 同时遍历,判断相同的位置上,所包含的元素是否相等。用 equals方法判断。
  4. 在判断长度是不是相等,如果不等,返回false.如果相等,就返回true.
    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());
    }

hashCode() 方法

ArrayList的hashCode()方法逻辑:

  • 初始化hashCode的值为1
  • 遍历包含的每一个元素,将当前hashCode的值乘以31. 加上下一个元素的hashCode值.下一个元素为null,那么它的hashCode值为0.
    /**
     * Returns the hash code value for this list.
     *
     * <p>This implementation uses exactly the code that is used to define the
     * list hash function in the documentation for the {@link List#hashCode}
     * method.
     *
     * @return the hash code value for this list
     */
    public int hashCode() {
        int hashCode = 1;
        for (E e : this)
            hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
        return hashCode;
    }

漂亮的移除中间片段

加入一个数组,只取中间一段,它的实现方法是:

  • 用 listIterator(fromIndex) 找到开始的地方。
  • 跳过要保留的长度。
  • 遍历剩下的元素,先移到下一个位置,再移除上一个位置的元素。
      /**
     * Removes from this list all of the elements whose index is between
     * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.
     * Shifts any succeeding elements to the left (reduces their index).
     * This call shortens the list by {@code (toIndex - fromIndex)} elements.
     * (If {@code toIndex==fromIndex}, this operation has no effect.)
     *
     * <p>This method is called by the {@code clear} operation on this list
     * and its subLists.  Overriding this method to take advantage of
     * the internals of the list implementation can <i>substantially</i>
     * improve the performance of the {@code clear} operation on this list
     * and its subLists.
     *
     * <p>This implementation gets a list iterator positioned before
     * {@code fromIndex}, and repeatedly calls {@code ListIterator.next}
     * followed by {@code ListIterator.remove} until the entire range has
     * been removed.  <b>Note: if {@code ListIterator.remove} requires linear
     * time, this implementation requires quadratic time.</b>
     *
     * @param fromIndex index of first element to be removed
     * @param toIndex index after last element to be removed
     */
   protected void removeRange(int fromIndex, int toIndex) {
        ListIterator<E> it = listIterator(fromIndex);
        for (int i=0, n=toIndex-fromIndex; i<n; i++) {
            it.next();
            it.remove();
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值