List
public interface List<E> extends Collection<E> |
Java设计List更多是对数组的扩展,如果不使用泛型,可以容纳任何类型的元素;如果使用泛型,只能容纳指定的类型元素;和数组相对的话,数组并不泛型,并且List的容量是可以动态扩展的。
List实现了Collection接口,已知实现类:AbstractList、AbstractSequentialList、ArrayList、AttributeList、CopyOnWriteArrayList、LinkedList、RoleList、RoleUnresolvedList、Stack、Vector,其中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 < 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 src, int 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; } |