Java集合(1) - List集合源码解析

List

public interface List<E> extends Collection<E>

 

      Java设计List更多是对数组的扩展,如果不使用泛型,可以容纳任何类型的元素;如果使用泛型,只能容纳指定的类型元素;和数组相对的话,数组并不泛型,并且List的容量是可以动态扩展的。

      List实现了Collection接口,已知实现类:AbstractListAbstractSequentialListArrayListAttributeListCopyOnWriteArrayListLinkedListRoleListRoleUnresolvedListStackVector,其中Vector和Stack已过时了。

      List中的元素是可以重复、有序,也可以包含null值的,可以对某个元素位于集合中的位置进行精确控制。

      List还提供了特殊的迭代器(ListIterator),除了允许Iterator接口提供的正常操作外,这个迭代器还允许元素插入和替换,及双向访问。

 

AbstractList

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>

 

      是List下的抽象类,并继承AbstractCollection,其中的next()和remove()方法底层分别由get(int index)和remove(int index)方法实现。对于按照顺序遍历访问元素的需求,使用List的Iterator就可以做到,而AbstractList是提供了该方法的通用实现。

      其中List的类型不同,元素的链接方式也不同,所以对于元素的增、删、查实现方式也不同,所以这一块并没有在Abstract中提供实现。

给了iterator()方法的通用实现,其中next()和remove()方法

代表方法:

方法

作用

备注

E get(int index);

返回列表中指定位置的元素

在AbstractList中以抽象方法存在。

也是AbstractList中唯一的抽象方法

ListIterator<E> listIterator();

按照适当的顺序,返回此列表元素的列表迭代器

在AbstractList中默认实现,

ArrayList、Vector和Stack使用该默认实现。

LinkedList重写了该实现。

List<E> subList(int fromIndex, int toIndex)

返回列表中指定的froIndex(包括)和toIndex(不包括)之间的视图。

实际返回的List是靠原来的List支持的。

在AbstractList中默认实现。

ArrayList、LinkedList使用该默认实现。

     

      备注:对于subList方法,原list和子list做的非结构性修改(不设计List的大小改变的修改),都会影响到彼此。对于结构性修改的话,若发生结构性修改的是返回子List,那原List的大小也会发生变化(modCount与expectedModCount同时变化,不会触发Fast-Fail机制),而发生结构性修改的是原List(不包括由返回的子List导致的改变),那么判断式l.modCount != expectedModCount将返回true,触发Fast-Fail机制,抛出ConcurrentModificationException。因此,若在调用subList返回了子List之后,修改原List的大小,那么之前产生的子List将会失效。

      如:list.subList(f,t).clear();

 

AbstractSequentialList

public abstract class AbstractSequentialList<E> extends AbstractList<E>

 

这是一个实现List并继承于AbstractList的抽象类。

      这个抽象类通过ListIterator实现了链表中,根据index索引值操作链表的全部方法。

      此外ArrayList通过System.arraycopy完成元素移动,实现了顺序表中,根据index索引值操作顺序表的全部函数。

 

ArrayList

public class ArrayList<E> extends AbstractList<E>

        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

 

      ArrayList不是线程安全的,只能用在单线程环境下,多线程环境下可以考虑用Collections.synchronizedList(List l)方法,得到一个线程安全的ArrayList类,也可以使用conrrent并发包下的CopyOnWriteArrayList类。

 

新增方法:

clone():返回ArrayList实例的副本。

ensureCapacity():增加ArrayList实例容量。

removeRange():批量移除列表中的元素。

trimToSize():将列表容量调整至当前大小。

 

序列化:

      ArrayList实现了Serializable接口,因此支持序列化,能够通过序列化传输,但是源码中可以看到内置的数组elementData是用transient关键字修饰,标识不需要被序列化,因为elementData存储的不是真正的元素对象,而是指向对象的地址,当对地址进行反序列化后旧找不到原来的对象了,但ArrayList内置元素最终还是会被序列化。

/**

     * 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

 

所以ArrayList在序列化和反序列化时,会调用writeObject/readObject()方法进行手工序列化,将该ArrayList中的元素(即0~size-1)和容量大小写出,这样做的好处是,只保存/传输有实际意义的元素,最大限度的节约了存储、传输和处理的开销。

     

/**

     * Save the state of the <tt>ArrayList</tt> instance to a stream (that

     * is, serialize it).

     *

     * @serialData The length of the array backing the <tt>ArrayList</tt>

     *             instance is emitted (int), followed by all of its elements

     *             (each an <tt>Object</tt>) in the proper order.

     */

    private void writeObject(java.io.ObjectOutputStream s)

        throws java.io.IOException{

        // Write out element count, and any hidden stuff

        int expectedModCount = modCount;

        s.defaultWriteObject();

 

        // Write out size as capacity for behavioural compatibility with clone()

        s.writeInt(size);

 

        // Write out all elements in the proper order.

        for (int i=0; i<size; i++) {

            s.writeObject(elementData[i]);

        }

 

        if (modCount != expectedModCount) {

            throw new ConcurrentModificationException();

        }

    }

 

    /**

     * Reconstitute the <tt>ArrayList</tt> instance from a stream (that is,

     * deserialize it).

     */

    private void readObject(java.io.ObjectInputStream s)

        throws java.io.IOException, ClassNotFoundException {

        elementData = EMPTY_ELEMENTDATA;

 

        // Read in size, and any hidden stuff

        s.defaultReadObject();

 

        // Read in capacity

        s.readInt(); // ignored

 

        if (size > 0) {

            // be like clone(), allocate array based upon size not capacity

            int capacity = calculateCapacity(elementData, size);

            SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);

            ensureCapacityInternal(size);

 

            Object[] a = elementData;

            // Read in all elements in the proper order.

            for (int i=0; i<size; i++) {

                a[i] = s.readObject();

            }

        }

    }

 

随机访问

      ArrayList实现了RandomAccess接口,支持快速随机访问,实际上就是通过下角标进行快速访问。

      RandomAccess接口是List实现所使用的标记接口,用来表明其支持快速随机访问,目的是允许一般算法更改其行为,从而在将其引用到随机或连续访问列表时,提供更好的性能。

      特别是对List遍历算法中,特别使用RandomAccess List算法,用在SequenceAccess List上的区别就很大的,即之前的数组和链表的区别。

*1、对于类的典型实例,这个循环:

 * <pre>

 *     for (int i=0, n=list.size(); i &lt; n; i++)

 *         list.get(i);

 * </pre>

 * 2、运行速度比这个循环快:

 * <pre>

 *     for (Iterator i=list.iterator(); i.hasNext(); )

 *         i.next();

 * </pre>

 

自增长

      ArrayList是基于数组实现的动态增长容器,默认容量是10,也可以在创建时,就在构造器中指定容大小,并且每个ArrayList实例都有一个容量,这个容量是指存储列表元素的数组大小,至少等于列表的大小。

/**

     * Default initial capacity.

     */

private static final int DEFAULT_CAPACITY = 10;

/**

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

        }

    }

 

      调用add()向ArrayList添加元素,其容量也会自动增长,自动增长带来的是数据向新数组的重新拷贝。

      从源码中可以看到,ArrayList进行扩容时,会将老数组中的元素重写拷贝一份到新的数组中,每次数组的容量增长为其容量的1.5倍 + 1。

在实际使用中,这种增长方式的代价很高,所以要避免数组的扩展,当可预知要保存的元素数量时,在创建ArrayList实例时,就指定其容量,以避免数组扩容的发生。

/**

     * 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;

}

……

private void ensureCapacityInternal(int minCapacity) {

        ensureExplicitCapacity(calculateCapacity(elementData, 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);

    }

 

      源码中还有另外一个add()方法,这个方法里面调用了raneCheckForAdd方法,这个方法时会在设计index的操作时,对边界进行检查。

/**

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

 

        ensureCapacityInternal(size + 1);  // Increments modCount!!

        System.arraycopy(elementData, index, elementData, index + 1,

                         size - index);

        elementData[index] = element;

        size++;

}

……

private void rangeCheckForAdd(int index) {

            if (index < 0 || index > this.size)

                throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        }

 

 

      也可以手动使用ensureCapacity操作来指定增加ArrayList容量,这可以减少递增式再分配的数量,但此方法不是同步的,如果有多个线程同时访问一个ArrayList实例,而其中至少一个线程从结构上修改(涉及调整底层数组大小),必须保持外部同步。

 

/**

     * Increases the capacity of this <tt>ArrayList</tt> instance, if

     * necessary, to ensure that it can hold at least the number of elements

     * specified by the minimum capacity argument.

     *

     * @param   minCapacity   the desired minimum capacity

     */

    public void ensureCapacity(int minCapacity) {

        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)

            // any size if not default element table

            ? 0

            // larger than default for default empty table. It's already

            // supposed to be at default size.

            : DEFAULT_CAPACITY;

 

        if (minCapacity > minExpand) {

            ensureExplicitCapacity(minCapacity);

        }

    }

 

  注:ensureCapacity()是不同步的,ArrayList的动态改变列表大小也是基于这个方法实现的,解决不同步的方法是,使用Collections.synchronizedList方法将该列表包装起来,最好在创建时完成,以防止对列表进行不同步的访问。

      尽量不要使用ensureCapacity方法,因为改变List的容量会降低效率,改变List容量的原理是常见一个比之前更大容量的List,将原有List的数据copy到新的List,在删除原有的List。

      追踪copy数组的代码时,可以看到,该方法实际上是在内部又创建了一个长度为newlength的数组,调用System.arraycopy()方法,将原来数组中的元素复制到了新的数组中。

      System.arraycopy()方法,给标记是native,调用的是系统C/C++代码,但是在openJDK中可以看到,实际上最终调用了C的memmove()方法,因此可以保证同一个数组内元素的正确复制和移动,比一般的复制方法的实现效率要高很多,很适合用来批量处理数组。

/**

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

}

……

@SuppressWarnings("unchecked")

    public static <T> T[] copyOf(T[] original, int newLength) {

        return (T[]) copyOf(original, newLength, original.getClass());

    }

……

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {

        @SuppressWarnings("unchecked")

        T[] copy = ((Object)newType == (Object)Object[].class)

            ? (T[]) new Object[newLength]

            : (T[]) Array.newInstance(newType.getComponentType(), newLength);

        System.arraycopy(original, 0, copy, 0,

                         Math.min(original.length, newLength));

        return copy;

    }

……

public static native void arraycopy(Object srcint  srcPos,

                                        Object dest, int destPos,

                                        int length);

 

      ArrayList还有一个很有趣的方法,可以将底层数组的容量调整为当前列表保存的元素的大小。

      在使用Arraylist过程中,由于ElementData的长度会被扩展,所以经常会出现size很小,但是elementData.length很大的情况,造成空间的浪费。

      ArrayList通过trimToSize返回一个新的数组给elementData,其中元素内容保持不变,length和size相同。

/**

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

        }

    }

 

Fail-Fast机制(快速失败)

线程1通过Iterator在遍历集合(5个元素)中的元素,在这个时候,线程2在集合的第2个位置插入了一个元素,那么这个时候程序就会触发Fail-Fast机制,抛出ConcurrentModificationException异常。同理,如果单线程违法了机制,同样也会抛出此异常。

当方法在面对对象并发修改,但不允许这种修改时,就会抛出异常,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险,即是Java对集合的一种错误检测机制。

 

      在ArrayList源码中,可以看到迭代器在调用next()、remove()方法时,都是调用checkForComodification()方法,该方法会判断modCount != expectedModCount,如果不等就触发Fail-Fast机制,抛出ConcurrentModificationException异常。

      继续追踪代码,会发现modCount是每次对集合进行结构化修改的操作次数,即在迭代前会得到该操作次数,当迭代时,发现次数不对,则会执行Fail-Fast机制。

private class Itr implements Iterator<E> {

        /**

         * Index of element to be returned by subsequent call to next.

         */

        int cursor = 0;

 

        /**

         * Index of element returned by most recent call to next or

         * previous.  Reset to -1 if this element is deleted by a call

         * to remove.

         */

        int lastRet = -1;

 

        /**

         * The modCount value that the iterator believes that the backing

         * List should have.  If this expectation is violated, the iterator

         * has detected concurrent modification.

         */

        int expectedModCount = modCount;

 

        public boolean hasNext() {

            return cursor != size();

        }

 

        public E next() {

            checkForComodification();

            try {

                int i = cursor;

                E next = get(i);

                lastRet = i;

                cursor = i + 1;

                return next;

            } catch (IndexOutOfBoundsException e) {

                checkForComodification();

                throw new NoSuchElementException();

            }

        }

 

        public void remove() {

            if (lastRet < 0)

                throw new IllegalStateException();

            checkForComodification();

 

            try {

                AbstractList.this.remove(lastRet);

                if (lastRet < cursor)

                    cursor--;

                lastRet = -1;

                expectedModCount = modCount;

            } catch (IndexOutOfBoundsException e) {

                throw new ConcurrentModificationException();

            }

        }

 

        final void checkForComodification() {

            if (modCount != expectedModCount)

                throw new ConcurrentModificationException();

        }

}

……

/**

     * The number of times this list has been <i>structurally modified</i>.

     * Structural modifications are those that change the size of the

     * list, or otherwise perturb it in such a fashion that iterations in

     * progress may yield incorrect results.

     *

     * <p>This field is used by the iterator and list iterator implementation

     * returned by the {@code iterator} and {@code listIterator} methods.

     * If the value of this field changes unexpectedly, the iterator (or list

     * iterator) will throw a {@code ConcurrentModificationException} in

     * response to the {@code next}, {@code remove}, {@code previous},

     * {@code set} or {@code add} operations.  This provides

     * <i>fail-fast</i> behavior, rather than non-deterministic behavior in

     * the face of concurrent modification during iteration.

     *

     * <p><b>Use of this field by subclasses is optional.</b> If a subclass

     * wishes to provide fail-fast iterators (and list iterators), then it

     * merely has to increment this field in its {@code add(int, E)} and

     * {@code remove(int)} methods (and any other methods that it overrides

     * that result in structural modifications to the list).  A single call to

     * {@code add(int, E)} or {@code remove(int)} must add no more than

     * one to this field, or the iterators (and list iterators) will throw

     * bogus {@code ConcurrentModificationExceptions}.  If an implementation

     * does not wish to provide fail-fast iterators, this field may be

     * ignored.

     */

protected transient int modCount = 0;

 

LinkedList

public class LinkedList<E>

    extends AbstractSequentialList<E>

    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

 

      LinkedList是List接口的双向链表实现,并为在列表开头和结尾的get、remove、insert元素提供了统一的访问操作,这些操作允许了LinkedList作为Stack(栈)、Queue(队列)、Deque(双向队列)的使用。

      LinkedList不是同步的,需要与外部保持同步,如果多个线程同时进行结构化修改,则需要使用Collections.synchronizedList来包装,防止意外的不同步访问。

      LinkedList的Iterator和ListIterator方法返回的迭代器都是Fast-Fail机制的,除非通过迭代器自身的remove或add方法,其他任何时间任何方式的修改都会导致ConcurrentModificationException。

      源码中可以看到,LinkedList也有Fast-Fail机制。

/**

     * Returns a list-iterator of the elements in this list (in proper

     * sequence), starting at the specified position in the list.

     * Obeys the general contract of {@code List.listIterator(int)}.<p>

     *

     * The list-iterator is <i>fail-fast</i>: if the list is structurally

     * modified at any time after the Iterator is created, in any way except

     * through the list-iterator's own {@code remove} or {@code add}

     * methods, the list-iterator will throw a

     * {@code ConcurrentModificationException}.  Thus, in the face of

     * concurrent modification, the iterator fails quickly and cleanly, rather

     * than risking arbitrary, non-deterministic behavior at an undetermined

     * time in the future.

     *

     * @param index index of the first element to be returned from the

     *              list-iterator (by a call to {@code next})

     * @return a ListIterator of the elements in this list (in proper

     *         sequence), starting at the specified position in the list

     * @throws IndexOutOfBoundsException {@inheritDoc}

     * @see List#listIterator(int)

     */

    public ListIterator<E> listIterator(int index) {

        checkPositionIndex(index);

        return new ListItr(index);

    }

 

    private class ListItr implements ListIterator<E> {

 

      LinkedList是继承于AbstractSequentialList的双向链表;实现了Deque接口,即可以当做双向队列使用;实现了Serializable接口,意味着LinkedList支持序列化。

      可以看到LinkedList也有writeObject()和readObject()序列化方法。

/**

     * Saves the state of this {@code LinkedList} instance to a stream

     * (that is, serializes it).

     *

     * @serialData The size of the list (the number of elements it

     *             contains) is emitted (int), followed by all of its

     *             elements (each an Object) in the proper order.

     */

    private void writeObject(java.io.ObjectOutputStream s)

        throws java.io.IOException {

        // Write out any hidden serialization magic

        s.defaultWriteObject();

 

        // Write out size

        s.writeInt(size);

 

        // Write out all elements in the proper order.

        for (Node<E> x = first; x != null; x = x.next)

            s.writeObject(x.item);

    }

 

    /**

     * Reconstitutes this {@code LinkedList} instance from a stream

     * (that is, deserializes it).

     */

    @SuppressWarnings("unchecked")

    private void readObject(java.io.ObjectInputStream s)

        throws java.io.IOException, ClassNotFoundException {

        // Read in any hidden serialization magic

        s.defaultReadObject();

 

        // Read in size

        int size = s.readInt();

 

        // Read in all elements in the proper order.

        for (int i = 0; i < size; i++)

            linkLast((E)s.readObject());

    }

 

      注:LinkedList和ArrayList不同的是,LinkedList没有实现RandomAccess,即不支持快速随机访问。

 

版本区别:

JDK1.6是一个带空头的循环双向链表,注意transient(序列化)

private transient Entry<E> header = new Entry<E>(null, null, null);

private transient int size = 0;

 

JDK1.7/1.8定义为Node类,注意transient(序列化)

/**

     * Pointer to first node.

     * Invariant: (first == null && last == null) ||

     *            (first.prev == null && first.item != null)

     */

    transient Node<E> first;

 

    /**

     * Pointer to last node.

     * Invariant: (first == null && last == null) ||

     *            (last.next == null && last.item != null)

     */

    transient Node<E> last;

 

结构:

      结合https://blog.csdn.net/Su_Levi_Wei/article/details/104905121讲到的数组和链表的区别。

      可以得出LinkedList的每一个节点都是Node类型的实例,每个Node都包含当前元素,当前元素的前后节点,这样就形成了环环相扣,具有双向链表的特点。

      相对来看,这种结构使得LinkedList插入和删除效率高,查询效率低。

private static class Node<E> {

        E item;

        Node<E> next;

        Node<E> prev;

 

        Node(Node<E> prev, E element, Node<E> next) {

            this.item = element;

            this.next = next;

            this.prev = prev;

        }

    }

 

查找:

      LinkedList的所有查找都是从靠近索引的那端开始(头尾),这样做的目的可以提高查找效率。

在拥有10个元素的情况下,从get(3)开始查找,那么会从头部开始查找。同理,如果是从get(8)开始查找,那么会从尾部开始查找。

 

/**

     * 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) {

        checkElementIndex(index);

        return node(index).item;

}

……

private void checkElementIndex(int index) {

        if (!isElementIndex(index))

            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

    }

 

    private void checkPositionIndex(int index) {

        if (!isPositionIndex(index))

            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

    }

……

/**

     * Returns the (non-null) Node at the specified element index.

     */

    Node<E> node(int index) {

        // assert isElementIndex(index);

 

        if (index < (size >> 1)) {

            Node<E> x = first;

            for (int i = 0; i < index; i++)

                x = x.next;

            return x;

        } else {

            Node<E> x = last;

            for (int i = size - 1; i > index; i--)

                x = x.prev;

            return x;

        }

    }

 

新增:

      Add方法在添加时,是添加到List的末尾,就是新建一个Node节点,将后面的那个节点Last作为新节点的First节点,后节点为null。

      将这个新Node节点作为整个List的后节点,如果之前的后节点为Null,将新建的Node作为List的前节点,否则List的后节点指针指向新建的Node,最后size+1,当前List操作数modCount+1。

      可以看处,在添加节点时,LinkedList只关心重要的数据,这大大的提高了插入效率,而且追加在末尾还保证了顺序,接下来在来看下指定位置插入。

/**

     * Appends the specified element to the end of this list.

     *

     * <p>This method is equivalent to {@link #addLast}.

     *

     * @param e element to be appended to this list

     * @return {@code true} (as specified by {@link Collection#add})

     */

    public boolean add(E e) {

        linkLast(e);

        return true;

}

……

/**

     * Links e as last element.

     */

    void linkLast(E e) {

        final Node<E> l = last;

        final Node<E> newNode = new Node<>(l, e, null);

        last = newNode;

        if (l == null)

            first = newNode;

        else

            l.next = newNode;

        size++;

        modCount++;

    }

 

      指定位置插入,会先检查是否超出边界,这个和ArrayList的检查边界是一个道理。

如果插入位置就是最后一个位置,那么则执行和上面的添加一样的过程。

否则,会搜索当前位置的Node,拿着查到succ节点的前作为本节点的前节点,而succ节点会作为本节点的后节点,这个succ节点的前置节点会替换成新建的Node节点。如果旧的前置节点pred为null,那么久直接将当前LinkedList的前置节点为新建的节点Node,否则就将旧的pred的下一个节点指针指向新建节点Node,完成链表相连,最后size+1,当前List操作数modCount+1.

与非指定位置添加,区别在于first和当前位置节点的prev(上)节点。

 

/**

     * 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) {

        checkPositionIndex(index);

 

        if (index == size)

            linkLast(element);

        else

            linkBefore(element, node(index));

}

……

/**

     * Returns the (non-null) Node at the specified element index.

     */

    Node<E> node(int index) {

        // assert isElementIndex(index);

 

        if (index < (size >> 1)) {

            Node<E> x = first;

            for (int i = 0; i < index; i++)

                x = x.next;

            return x;

        } else {

            Node<E> x = last;

            for (int i = size - 1; i > index; i--)

                x = x.prev;

            return x;

        }

    }

……

/**

     * Inserts element e before non-null Node succ.

     */

    void linkBefore(E e, Node<E> succ) {

        // assert succ != null;

        final Node<E> pred = succ.prev;

        final Node<E> newNode = new Node<>(pred, e, succ);

        succ.prev = newNode;

        if (pred == null)

            first = newNode;

        else

            pred.next = newNode;

        size++;

        modCount++;

    }

 

删除:

      可以看到也是先去检查边界,检查完就查找到当前节点。

      判断查找到的x节点的前后节点是否为空,不为空把前后节点之间在相连起来即可,最后在把当前节点、前后节点置空,size-1,操作数+1。

      可以看到只需要移动当前节点的前后节点数据即可,所以效率也是很高。

/**

     * Removes the element at the specified position in this list.  Shifts any

     * subsequent elements to the left (subtracts one from their indices).

     * Returns the element that was removed from the list.

     *

     * @param index the index of the element to be removed

     * @return the element previously at the specified position

     * @throws IndexOutOfBoundsException {@inheritDoc}

     */

    public E remove(int index) {

        checkElementIndex(index);

        return unlink(node(index));

}

……

/**

     * Unlinks non-null node x.

     */

    E unlink(Node<E> x) {

        // assert x != null;

        final E element = x.item;

        final Node<E> next = x.next;

        final Node<E> prev = x.prev;

 

        if (prev == null) {

            first = next;

        } else {

            prev.next = next;

            x.prev = null;

        }

 

        if (next == null) {

            last = prev;

        } else {

            next.prev = prev;

            x.next = null;

        }

 

        x.item = null;

        size--;

        modCount++;

        return element;

    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值