arrayList 的底层实现解析

1 start

2 arrayList 的底层实现

2.1 数据结构

动态数组中,存储数据用的是一段连续的大内存,所以如果我们要在某一个位置添加或者删除一个元素,剩下的每个元素都要相应地往前或往后移动。

2.2 add(),remove()

实现两个方法的底层核心是Arrays.copyOf(),方法返回的数组是新的数组对象,原数组对象仍是原数组对象,不变,该拷贝不会影响原来的数组。
Arrays.copyOf()又由本地方法 System.arraycopy() 实现;

2.2.1 System.arraycopy() 抽象方法
    /**
     * @param      src      the source array.
     * @param      srcPos   starting position in the source array.
     * @param      dest     the destination array.
     * @param      destPos  starting position in the destination data.
     * @param      length   the number of array elements to be copied.
     * @exception  IndexOutOfBoundsException  if copying would cause
     *               access of data outside array bounds.
     * @exception  ArrayStoreException  if an element in the <code>src</code>
     *               array could not be stored into the <code>dest</code> array
     *               because of a type mismatch.
     * @exception  NullPointerException if either <code>src</code> or
     *               <code>dest</code> is <code>null</code>.
     */
    public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);
2.2.2 add()方法实现

普通添加:

    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
    	// 设置底层数组真实容量
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // 设置数组长度属性,加1;
        // 数组最后一个元素赋值;
        elementData[size++] = e;
        return true;
    }

指定位置添加:

    /**
     * Inserts the specified element at the specified position in this
     * list. Shifts the element currently at that position (if any) and
     * any subsequent elements to the right (adds one to their indices).
     *
     * @param index index at which the specified element is to be inserted
     * @param element element to be inserted
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public void add(int index, E element) {
        rangeCheckForAdd(index); // 校验index是否越界
        // 使用arrays.copy,更新数组容量
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // 再次使用arrays.copy,更新数组元素
        System.arraycopy(elementData, index, elementData, index + 1,size - index); // index之后元素整体后移
        elementData[index] = element; // index位置插入元素
        size++; // 数组长度属性加1
    }

    /**
     * A version of rangeCheck used by add and addAll.
     */
    private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
	// 设置数组真实容量
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    // 获取数组最小容量
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        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);
    }
    /**
     * The maximum size of array to allocate.
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * 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;
        // 容量变为之前的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;
    }
2.2.3 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); // 校验index

        modCount++; // 数组结构操作次数,加1
        E oldValue = elementData(index); // 获取被删除的元素

		// 获取需要移动的元素的数量
        int numMoved = size - index - 1;
        // 复制数组元素
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index, numMoved);
        // 数组长度属性减1
        // 最后一个元素置空
        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
     */
    // 从前往后,找到并删除第一个元素,不管null与否
    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) {
    	// 数组结构操作次数,加1
        modCount++;
        // 数组元素移动数量
        int numMoved = size - index - 1;
        // 通过复制,移动元素
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        // 数组长度属性减1
        // 最后一个元素置空
        elementData[--size] = null; // clear to let GC do its work
    }

2.3 iterator(foreach)遍历

2.3.1 遍历分为两步:
//        Iterator iterator = formList.iterator();
//        if (iterator.hasNext()) {
//            Object next = iterator.next();
//
//        }
        // 第一步,判断是否有下个元素
        public boolean hasNext() {
        // 校验当前内存位置是否超出数组地址长度
            return cursor != size;
        }

		// 第二步,下个元素是什么,并赋值给上面例子中的item变量
        @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];
        }
        // 校验底层数组的数据结构变化次数,
        // 新增或删除元素是modeCount++,导致iterator遍历时,抛出并发修改异常;
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
2.3.2 ConcurrentModificationException

Iterator是工作在一个独立的线程中,并且拥有一个 mutex锁,就是说Iterator在工作的时候,是不允许被迭代的对象被改变的。Iterator被创建的时候,建立了一个内存索引表(单链表),这 个索引表指向原来的对象,当原来的对象数量改变的时候,这个索引表的内容没有同步改变,所以当索引指针往下移动的时候,便找不到要迭代的对象,于是产生错 误。
避免该异常的办法有两种,1.遍历完在删除;2.iterator.remove();

//        Iterator iterator = formList.iterator();
//        if (iterator.hasNext()) {
//            Object next = iterator.next();
//            iterator.remove();
//
//        }

Iterator.remove()实现:

// expectedModCount = modCount,保证了两者的相等
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();
            }
        }

2.4 get()

2.5 substring()

具有相同的底层数组;操作子数组会影响原来的数组;

2.4 异常

java.util.ConcurrentModificationException; // 并发修改异常:同时iterator遍历,同时add或remove触发
IndexOutOfBoundsException(); // 下标越界异常:add(index,object),index指定数据越界
new NoSuchElementException(); // 无此数据异常:iterator遍历数组时地址超出界限,一般不会发生;
ArrayStoreException // 数据类型不匹配异常:add时数据类型不匹配;
NullPointerException // 空指针异常:add时,源数组为null;

2.5 error

new OutOfMemoryError(); // 内存益处错误:add数据时,内存溢出;

扫描二维码关注公众号,回复: 10179395 查看本文章
发布了101 篇原创文章 · 获赞 21 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/leinminna/article/details/105110253