[java] ArrayList实现原理

本篇分析基于jdk1.8。

数组与链表的基本知识在这里不作展开,属于数据结构与算法的重点内容。

直接上几个ArrayList里的方法。

1.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!!
        elementData[size++] = e;
        return true;
    }

忽略返回值做了两件事情。

  • 确保当前数组的容量
  • 将需要添加的元素,也就是这里的e加到数组的末尾。size当前数组的大小,size++自然就是当前数组的后一位。

其他值得注意的是,此方法并没有做任何同步处理。并且,事实上整个Arraylist里的所有方法都没有做任何同步处理。因此这些涉及数据操作的方法都不是线程安全的。

另外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

非常简单,就是一个对象数组而已。注意,它使用了transient关键字来修饰,这意味着ArrayList不会被序列化。

2.  Get方法

 /**
     * 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);
    }

做了两件事情

  • 检测传入的参数,即数组的下标,是否越界
  • 若通过检测,则返回对应位置的元素即可,因为数组是有序排列的。

3. remove方法

扫描二维码关注公众号,回复: 2319237 查看本文章
/**
     * 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;
    }

这个实现很有意思的是它判断了一下要删除的对象是否是null,为什么要做这个判断呢,因为他们判断对象是否相等的方法不一样。null直接用两个等号判断,而非null对象,则是用equals方法判断相等。

遍历数组,找到相等对象,确定下标,然后删除当前数组中该下标的元素。删除方法为fastRemove,实现如下

/*
     * 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
    }

由于是删除,所以不需要做越界检查。结构变化数modCount加一,计算需要移动的数据元素,为当前数组大小,减去删除元素位置,再减去1,为什么要减去1因为数组下标从0开始,很好理解,做个草图如下:

当前数组元素一共9个,然后假设我要删除的是第6个元素。即size=9,index=5(从0开始)。要删除的元素是6,需要移动的元素即为7,4,8一共三个。

size-index-1 = 9 - 5 -1 = 3.

计算正确,一个测试用例诞生~。

然后将五个参数传入了System类下的arraycopy方法。这个方法的作用就是根据需要移动的元素,重新构造一个没有要删除元素的新数组,也即实现了删除的功能。这里额外插一句,从clean code的角度来讲,这个方法的参数已经太多了,clean code的建议是三个最多,只能说凡事无绝对吧。或者也可以说jdk的代码也并非从各个角度来看都完美无缺。

方法arraycopy在System类看不到实现,它是一个native方法。java众多的底层方法,实现几乎都是native方法,也就是用别的语言实现的。作为一个现代的程序员,或者说作为一个流水线上的程序员,大多数人真的一辈子也许也不需要知道这个方法到底如何实现的。

那么,如果要使用线程安全的容器,应该使用哪一个呢。答案就是Vector。

Vector的add方法如下:

/**
     * Appends the specified element to the end of this Vector.
     *
     * @param e element to be appended to this Vector
     * @return {@code true} (as specified by {@link Collection#add})
     * @since 1.2
     */
    public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }

用了synchronized直接修饰这个方法。除此之外实现逻辑和ArrayList几乎一样。modCount从注释来看这个变量代表的是这个容器的结构变化次数,所谓结构变化很好理解,就是数组中元素增加了一个,减少了一个,就是结构的变化。这里把modCount放在这里做计算,是一个和ArrayList不一样的地方,ArrayList是在check的时候来做。但这仅仅是代码风格的不统一。

Vector的get方法如下:

/**
     * Returns the element at the specified position in this Vector.
     *
     * @param index index of the element to return
     * @return object at the specified index
     * @throws ArrayIndexOutOfBoundsException if the index is out of range
     *            ({@code index < 0 || index >= size()})
     * @since 1.2
     */
    public synchronized E get(int index) {
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);

        return elementData(index);
    }

也是用sychronized修饰了这个方法,然后先检查是否越界,不越界则返回,但是它没有像ArrayList一样将这个check提出一个方法,不太明白为何jdk的两个关联性非常强的类还会存在实现方法不一样,代码风格不一致的问题,虽然从功能上讲没有任何区别。与ArrayList基本一样。

Vector的remove方法如下:

/**
     * Removes the first (lowest-indexed) occurrence of the argument
     * from this vector. If the object is found in this vector, each
     * component in the vector with an index greater or equal to the
     * object's index is shifted downward to have an index one smaller
     * than the value it had previously.
     *
     * <p>This method is identical in functionality to the
     * {@link #remove(Object)} method (which is part of the
     * {@link List} interface).
     *
     * @param   obj   the component to be removed
     * @return  {@code true} if the argument was a component of this
     *          vector; {@code false} otherwise.
     */
    public synchronized boolean removeElement(Object obj) {
        modCount++;
        int i = indexOf(obj);
        if (i >= 0) {
            removeElementAt(i);
            return true;
        }
        return false;
    }

indexOf方法实现如下:

/**
     * Returns the index of the first occurrence of the specified element in
     * this vector, searching forwards from {@code index}, or returns -1 if
     * the element is not found.
     * More formally, returns the lowest index {@code i} such that
     * <tt>(i&nbsp;&gt;=&nbsp;index&nbsp;&amp;&amp;&nbsp;(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i))))</tt>,
     * or -1 if there is no such index.
     *
     * @param o element to search for
     * @param index index to start searching from
     * @return the index of the first occurrence of the element in
     *         this vector at position {@code index} or later in the vector;
     *         {@code -1} if the element is not found.
     * @throws IndexOutOfBoundsException if the specified index is negative
     * @see     Object#equals(Object)
     */
    public synchronized int indexOf(Object o, int index) {
        if (o == null) {
            for (int i = index ; i < elementCount ; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = index ; i < elementCount ; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

removeElementAt方法实现如下:

/**
     * Deletes the component at the specified index. Each component in
     * this vector with an index greater or equal to the specified
     * {@code index} is shifted downward to have an index one
     * smaller than the value it had previously. The size of this vector
     * is decreased by {@code 1}.
     *
     * <p>The index must be a value greater than or equal to {@code 0}
     * and less than the current size of the vector.
     *
     * <p>This method is identical in functionality to the {@link #remove(int)}
     * method (which is part of the {@link List} interface).  Note that the
     * {@code remove} method returns the old value that was stored at the
     * specified position.
     *
     * @param      index   the index of the object to remove
     * @throws ArrayIndexOutOfBoundsException if the index is out of range
     *         ({@code index < 0 || index >= size()})
     */
    public synchronized void removeElementAt(int index) {
        modCount++;
        if (index >= elementCount) {
            throw new ArrayIndexOutOfBoundsException(index + " >= " +
                                                     elementCount);
        }
        else if (index < 0) {
            throw new ArrayIndexOutOfBoundsException(index);
        }
        int j = elementCount - index - 1;
        if (j > 0) {
            System.arraycopy(elementData, index + 1, elementData, index, j);
        }
        elementCount--;
        elementData[elementCount] = null; /* to let gc do its work */
    }

实现原理很简单,根据要删除的对象找到它的下标i,然后根据这个下标删除第i个元素即可。除了加上了锁,以及一些代码风格的,与ArrayList没有区别。

其他的方法不作仔细分析,到这里可以明确的是,对于Arraylist与Vector最常用的方法,以及其实现原理,以及他们之间的差异,都已经非常清楚了。

猜你喜欢

转载自blog.csdn.net/topdeveloperr/article/details/81143210