From 13f1dff327e4a966d8a59e7ddfbd48ee853972ce Mon Sep 17 00:00:00 2001 From: RDCynthia Date: Wed, 4 Mar 2020 10:26:26 +0800 Subject: [PATCH 1/4] Commit ArrayList Homework --- second/week_01/81/ArrayaList.md | 1795 +++++++++++++++++++++++++++++++ 1 file changed, 1795 insertions(+) create mode 100644 second/week_01/81/ArrayaList.md diff --git a/second/week_01/81/ArrayaList.md b/second/week_01/81/ArrayaList.md new file mode 100644 index 0000000..0aebc68 --- /dev/null +++ b/second/week_01/81/ArrayaList.md @@ -0,0 +1,1795 @@ +ArrayList 底层由数组实现,并可根据元素数量自动扩容。 + +有一个重点是,ArrayList 是非线程安全的,如果全局并发调用会产生意想不到的结果,虽然 ArrayList 已经尽最大努力(从源码中可以看出,大部分方法都在尽全力验证状态一致性)保证其快速失败。 + +#### 简述 + +``` +public class ArrayList +extends AbstractList +implements List, RandomAccess, Cloneable, Serializable +``` + +List 接口由大小可调整的数组实现。实现所有可选的列表操作,并允许所有元素,包括null。除了实现List接口之外,此类还提供一些方法来操纵内部用于存储列表的数组的大小。此类与Vector大致等效,但List是不同步的。 + +size()、isEmpty()、get()、set()、iterator()、listIterator()操作时间复杂度为 O(1)。add()操作摊销固定时间,也就是说,添加 n 个元素,时间复杂度为 O(n)。所有其他操作均以线性时间运行(大致而言)。与LinkedList实现相比,常数因子较低。 + +每个ArrayList实例都有一个 capacity(容量),capacity是列表中存储元素的数组的大小,它总是至少与列表大小一样大,随着元素添加到ArrayList中,其容量会自动增长。除了添加元素具有固定的摊销时间成本外,没有指定增长策略的详细信息。 + +应用程序可以使用 sureCapacity() 操作在添加大量元素之前增加 ArrayList 实例的容量,这可以减少自动扩容重新分配的数量。 + +请注意,此实现是不同步的。如果多个线程同时访问ArrayList实例,并且至少有一个线程在结构上修改列表,则必须在外部进行同步。(结构修改是指添加或删除一个或多个元素或显式调整内部数组大小的任何操作,仅设置元素的值不属于结构修改。)通常,通过在自然封装列表的某个对象上进行同步来完成此操作,如果不存在这样的对象,则应使用 `Collections.synchronizedList` 方法“包装”列表,且最好在创建时完成此操作,以防止意外的不同步访问列表: + +``` +List list = Collections.synchronizedList(new ArrayList(...)); +``` + +此类的迭代器和 `listIterator` 方法返回的迭代器是快速失败的 : 如果在创建迭代器后的任何时间以任何方式对列表进行结构修改,除非通过迭代器自己的 `remove` 或 `add` 方法,否则迭代器将抛出 `ConcurrentModificationException`。 因此,面对并发修改,迭代器将快速而干净地失败,而不是在未来的不确定时间内冒任意和不确定行为的风险。 + +注意,迭代器的快速失败行为无法得到保证,因为通常来说,在存在不同步的并发修改的情况下,不可能做出任何严格的保证。 快速失败的迭代器会尽最大努力抛出 `ConcurrentModificationException`。 因此,编写依赖于此异常而判断正确性的程序是错误的 : 迭代器的快速失败行为应仅用于检测错误。 + +#### 源码分析 + +阅读源码前,首先说一下其中的一个很重要的参数 `modCount`。这个参数定义在 `java.util.AbstractList` 抽象类中,其作用是: + +> 记录已对该列表进行结构修改的次数。 +> 结构修改是指更改列表大小或以其他方式干扰列表大小的方式,使其正在进行的迭代可能会产生错误的结果。 +> iterator 和 listIterator 方法返回的迭代器和列表迭代器的实现使用了此字段。如果此字段的值在迭代期间意外更改,则迭代器或列表迭代器的next、remove、previous、set、add方法将抛出ConcurrentModificationException。 +> 面对迭代期间的并发修改,这提供了快速故障行为,而不是不确定的行为。 + +也就是说,由于 ArrayList 是非线程安全的,如果我们用在并发线程调用上,ArrayList 会使用 modCount 计数的方式,最大努力保证其方法的快速失败,而不会再将来产生不确定因素。 +但是,这个不是绝对有保证的,所以不应该并发的使用 ArrayList。 + +```java +package java.util; + +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; +import sun.misc.SharedSecrets; + +public class ArrayList extends AbstractList + implements List, 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. + * + * 共享的空数组实例,用于默认大小的空实例。我们将此与 EMPTY_ELEMENTDATA 区别开来,以了解添加第一个元素时需要扩充多少。 + */ + // EMPTY_ELEMENTDATA 与 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的区别: + // ArrayList 并不在初始化类时对容量大小进行初始化,而是在添加第一个元素的时候,但在初始化类时,通过以上两个参数,指定了在添加第一个元素时,容量扩容的大小。 + // 如果通过空构造函数进行类初始化,则使用DEFAULTCAPACITY_EMPTY_ELEMENTDATA,表示在添加第一个元素时,默认容量大小为 DEFAULT_CAPACITY ,也就是 10. + // 如果通过其他构造函数初始化类,则使用 EMPTY_ELEMENTDATA ,表示扩容时,使用传入的指定参数或集合的大小。 + 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. + * + * 存储 ArrayList 的元素的数组缓冲区。 + * ArrayList 的 capacity 是此数组缓冲区的长度。 + * 添加第一个元素时,任何具有 elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的空 ArrayList 都将扩展为 DEFAULT_CAPACITY。 + */ + // 实际存储数据的数组 + transient Object[] elementData; // non-private to simplify nested class access + + /** + * The size of the ArrayList (the number of elements it contains). + * + * ArrayList 的大小(其包含元素的数量) + * + * @serial + */ + // 在后面会看到 elementData.length。这个 length 指的是数组长度,而 size 指的是数组中元素的个数。所以 length >= size 。 + private int size; + + /** + * 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 + * + * 构造一个指定初始化容量大小的空 list。如果参数为负数,则会抛出异常。 + */ + public ArrayList(int initialCapacity) { + // 如果参数大于 0,则创建一个 initialCapacity 大小的 Object 数组 + if (initialCapacity > 0) { + this.elementData = new Object[initialCapacity]; + // 如果参数等于 0,则使用默认空数组,EMPTY_ELEMENTDATA + } else if (initialCapacity == 0) { + this.elementData = EMPTY_ELEMENTDATA; + // 如果参数为负数,则抛出异常。 + } else { + throw new IllegalArgumentException("Illegal Capacity: "+ + initialCapacity); + } + } + + /** + * Constructs an empty list with an initial capacity of ten. + * + * 使用默认容量大小 10 构造一个空 list。 + */ + // 这就是上面说的 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 与 EMPTY_ELEMENTDATA 的区别。 + public ArrayList() { + this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; + } + + /** + * Constructs a list containing the elements of the specified + * collection, in the order they are returned by the collection's + * iterator. + * + * 通过包含指定参数集合元素 collection 创建一个 list。其内部顺序由 collection 的 iterator 指定。 + * 如果参数为 null,则会抛出异常。 + * + * @param c the collection whose elements are to be placed into this list + * @throws NullPointerException if the specified collection is null + */ + public ArrayList(Collection c) { + //将参数集合转为数组 + elementData = c.toArray(); + //如果数组长度不为 0 + if ((size = elementData.length) != 0) { + // c.toArray might (incorrectly) not return Object[] (see 6260652) + // 在使用toArray方法时,返回的不一定是 Object 类型的数组。比如使用 Arrays.aslist() 方法,其返回的是其内部类型的数组。 + // 具体情况可以参考 JDK-6260652 :https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-6260652 + if (elementData.getClass() != Object[].class) + //转为 Object 的数组。 + elementData = Arrays.copyOf(elementData, size, Object[].class); + } else { + // 如果数组长度为 0,则替换为默认空数组 EMPTY_ELEMENTDATA + // replace with empty array. + this.elementData = EMPTY_ELEMENTDATA; + } + } + + /** + * Trims the capacity of this ArrayList instance to be the + * list's current size. An application can use this operation to minimize + * the storage of an ArrayList instance. + * + * 将此ArrayList实例的容量调整为列表的当前大小。 应用程序可以使用此操作来最小化ArrayList实例的存储。 + */ + public void trimToSize() { + //记录结构性修改的次数 + modCount++; + //因为并发修改的可能,会造成 elementData 与 size 状态不统一,所以需要判断。这里也体现了尽最大努力的保证。 + if (size < elementData.length) { + //如果 size == 0,则返回默认空数组 EMPTY_ELEMENTDATA ,否则重现调整 elementData 的大小。 + elementData = (size == 0) + ? EMPTY_ELEMENTDATA + //copyOf在底层是创建新数组,然后复制过去。而复制的操作是 native 的,也就是由其他语言底层实现。 + : Arrays.copyOf(elementData, size); + } + } + + /** + * Increases the capacity of this ArrayList instance, if + * necessary, to ensure that it can hold at least the number of elements + * specified by the minimum capacity argument. + * + * 如有必要,增加此ArrayList实例的容量,以确保它至少可以容纳最小容量参数指定的元素数量。 + * + * @param minCapacity the desired minimum capacity + */ + //在一次性添加大量元素前,可以手动调用此方法先进行扩容,以减少自动扩容时分配过大的容量空间。 + public void ensureCapacity(int minCapacity) { + //先判断最小扩容的大小,如果是 DEFAULTCAPACITY_EMPTY_ELEMENTDATA ,则默认 DEFAULT_CAPACITY ,也就是 10,否则为 0。 + 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); + } + } + + //用于内部自动扩容时,其最小扩容大小。 + private static int calculateCapacity(Object[] elementData, int minCapacity) { + //如果 elementData 是空列表,并且属于 DEFAULTCAPACITY_EMPTY_ELEMENTDATA,则返回 DEFAULT_CAPACITY 与 指定扩容大小的最大值。 + if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { + return Math.max(DEFAULT_CAPACITY, minCapacity); + } + return minCapacity; + } + + //用于内部自动扩容。 + private void ensureCapacityInternal(int minCapacity) { + ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); + } + + //扩容的通用方法 + private void ensureExplicitCapacity(int minCapacity) { + //结构性修改计数 + modCount++; + + // overflow-conscious code 有溢出意识的代码 + //当指定扩容大小 > 当前数组 length 时,才进行扩容 + 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 + * + * 要分配的最大数组大小。 某些 VM 在数组中保留一些标头字。 尝试分配更大的数组可能会导致 OutOfMemoryError:请求的数组大小超出 VM 限制 + */ + 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; + // 默认扩容大小 = 当前数组大小 + (当前数组大小 / 2) + 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: + //由于扩容大部分都是动态自动扩容,其大小与元素数量 size 值差不多,所以使用数组复制即可。 + elementData = Arrays.copyOf(elementData, newCapacity); + } + + // 获取数组能扩容的最大值 + private static int hugeCapacity(int minCapacity) { + //不能为负值,否则会内存溢出 + if (minCapacity < 0) // overflow + throw new OutOfMemoryError(); + //如果指定的扩容值 > Integer.MAX_VALUE - 8,则返回 Integer.MAX_VALUE ,否则就使用 Integer.MAX_VALUE - 8 + return (minCapacity > MAX_ARRAY_SIZE) ? + Integer.MAX_VALUE : + MAX_ARRAY_SIZE; + } + + /** + * Returns the number of elements in this list. + * + * 返回列表中元素的数量 + * + * @return the number of elements in this list + */ + public int size() { + return size; + } + + /** + * Returns true if this list contains no elements. + * + * 如果列表中不包含任何元素,则返回 true + * + * @return true if this list contains no elements + */ + public boolean isEmpty() { + return size == 0; + } + + /** + * Returns true if this list contains the specified element. + * More formally, returns true if and only if this list contains + * at least one element e such that + * (o==null ? e==null : o.equals(e)). + * + * 如果此列表包含指定的元素,则返回true。 更正式地说,当且仅当此列表包含至少一个元素(e == null ? e == null :o.equals(e)) 时,才返回true。 + * + * @param o element whose presence in this list is to be tested + * @return true if this list contains the specified element + */ + public boolean contains(Object o) { + // 通过 indexOf 的返回值是否大于 0,来判断是否存在元素 + return indexOf(o) >= 0; + } + + /** + * Returns the index of the first occurrence of the specified element + * in this list, or -1 if this list does not contain the element. + * More formally, returns the lowest index i such that + * (o==null ? get(i)==null : o.equals(get(i))), + * or -1 if there is no such index. + * + * 返回指定元素在此列表中首次出现的索引;如果此列表不包含该元素,则返回-1。 + * 更正式地,返回最低索引 i,使得 (o==null ? get(i)==null : o.equals(get(i))),或者如果没有这样的索引,则返回-1。 + */ + public int indexOf(Object o) { + //如果参数为 null,则返回第一个 null 值的位置 + if (o == null) { + for (int i = 0; i < size; i++) + if (elementData[i]==null) + return i; + //否则返回第一个值相等的位置 + } else { + for (int i = 0; i < size; i++) + if (o.equals(elementData[i])) + return i; + } + //如果找不到,则返回 -1 + return -1; + } + + /** + * Returns the index of the last occurrence of the specified element + * in this list, or -1 if this list does not contain the element. + * More formally, returns the highest index i such that + * (o==null ? get(i)==null : o.equals(get(i))), + * or -1 if there is no such index. + * + * 返回指定元素在此列表中最后一次出现的索引;如果此列表不包含该元素,则返回-1。 + * 更正式地,返回最高索引 i,以使 (o==null ? get(i)==null : o.equals(get(i)));如果没有这样的索引,则返回- 1。 + */ + public int lastIndexOf(Object o) { + if (o == null) { + for (int i = size-1; i >= 0; i--) + if (elementData[i]==null) + return i; + } else { + for (int i = size-1; i >= 0; i--) + if (o.equals(elementData[i])) + return i; + } + return -1; + } + + /** + * Returns a shallow copy of this ArrayList instance. (The + * elements themselves are not copied.) + * + * 返回此 ArrayList 实例的浅副本。(元素本身不会被复制。) + * + * @return a clone of this ArrayList instance + */ + public Object clone() { + try { + // clone 一个浅复制的副本,其中不包含元素。 + ArrayList v = (ArrayList) super.clone(); + // 将数组单独复制一份新的 + v.elementData = Arrays.copyOf(elementData, size); + // 结构性修改计数清零 + v.modCount = 0; + return v; + } catch (CloneNotSupportedException e) { + // this shouldn't happen, since we are Cloneable + throw new InternalError(e); + } + } + + /** + * Returns an array containing all of the elements in this list + * in proper sequence (from first to last element). + * + *

The returned array will be "safe" in that no references to it are + * maintained by this list. (In other words, this method must allocate + * a new array). The caller is thus free to modify the returned array. + * + *

This method acts as bridge between array-based and collection-based + * APIs. + * + * 以正确的顺序(从第一个元素到最后一个元素)返回一个包含此列表中所有元素的数组。 + * 返回的数组将是“安全的”,因为此列表不保留对其的引用。换句话说,此方法必须分配一个新数组。 因此,调用者可以自由修改返回的数组。 + * 此方法充当基于数组的API和基于集合的API之间的桥梁。 + * + * @return an array containing all of the elements in this list in + * proper sequence + */ + public Object[] toArray() { + return Arrays.copyOf(elementData, size); + } + + /** + * Returns an array containing all of the elements in this list in proper + * sequence (from first to last element); the runtime type of the returned + * array is that of the specified array. If the list fits in the + * specified array, it is returned therein. Otherwise, a new array is + * allocated with the runtime type of the specified array and the size of + * this list. + * + *

If the list fits in the specified array with room to spare + * (i.e., the array has more elements than the list), the element in + * the array immediately following the end of the collection is set to + * null. (This is useful in determining the length of the + * list only if the caller knows that the list does not contain + * any null elements.) + * + * 返回一个数组,该数组按适当顺序(从第一个元素到最后一个元素)包含此列表中的所有元素。 + * 返回数组的运行时类型是指定数组的运行时类型。 如果列表适合指定的数组,则将其返回。 否则,将使用指定数组的运行时类型和此列表的大小分配一个新数组。 + * 如果列表适合指定的数组并有剩余空间(即,数组中的元素多于列表),则紧接集合结束后的数组中的元素设置为 null。仅当调用者知道列表不包含任何null元素时,对其确定列表的长度很有用。 + * + * @param a the array into which the elements of the list are to + * be stored, if it is big enough; otherwise, a new array of the + * same runtime type is allocated for this purpose. + * @return an array containing the elements of the list + * @throws ArrayStoreException if the runtime type of the specified array + * is not a supertype of the runtime type of every element in + * this list + * @throws NullPointerException if the specified array is null + */ + @SuppressWarnings("unchecked") + public T[] toArray(T[] a) { + //如果指定数组的大小 < 当前数组的大小,则创建一个类型 T ,大小 size 的新数组。 + if (a.length < size) + // Make a new array of a's runtime type, but my contents: + return (T[]) Arrays.copyOf(elementData, size, a.getClass()); + //否则先将 elementData 中的所有元素复制到 a 数组中。 + System.arraycopy(elementData, 0, a, 0, size); + //当 a 数组的长度大于 elementData 的长度时,将 elementData 在 a 数组中的最后一个元素的下一位置空。 + if (a.length > size) + a[size] = null; + return a; + } + + // Positional Access Operations 位置访问操作 + + // 返回指定下标的元素(这个方法是包访问权限) + @SuppressWarnings("unchecked") + E elementData(int index) { + return (E) elementData[index]; + } + + /** + * 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); + } + + /** + * Replaces the element at the specified position in this list with + * the specified element. + * + * 用指定元素替换此列表中指定位置的元素。 + * + * @param index index of the element to replace + * @param element element to be stored at the specified position + * @return the element previously at the specified position + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public E set(int index, E element) { + //边界检查 + rangeCheck(index); + + //先获取老的元素,用于返回 + E oldValue = elementData(index); + elementData[index] = element; + return oldValue; + } + + /** + * Appends the specified element to the end of this list. + * + * 将指定的元素追加到此列表的末尾。 + * + * @param e element to be appended to this list + * @return true (as specified by {@link Collection#add}) + */ + public boolean add(E e) { + //扩容操作 + ensureCapacityInternal(size + 1); // Increments modCount!! 结构性修改计数会增加 + //设置值,并增加 size 值 + 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); + + // 扩容操作 + ensureCapacityInternal(size + 1); // Increments modCount!! + // 将插入点及其后面的所有值,向后移动一位 + System.arraycopy(elementData, index, elementData, index + 1, + size - index); + // 插入该值 + elementData[index] = element; + // size 增加 + size++; + } + + /** + * 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); + + //结构性修改计数 + modCount++; + //获取老值用于返回 + E oldValue = elementData(index); + + //需要向左移动的元素数量 + int numMoved = size - index - 1; + //如果数量大于 0,则将 index + 1 处及其后面所有的元素,向左移动一位 + if (numMoved > 0) + System.arraycopy(elementData, index+1, elementData, index, + numMoved); + //将最后一位置空,用于垃圾回收 + 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 + * i such that + * (o==null ? get(i)==null : o.equals(get(i))) + * (if such an element exists). Returns true if this list + * contained the specified element (or equivalently, if this list + * changed as a result of the call). + * + * 如果存在指定元素,则从该列表中删除该元素的第一次出现。 如果列表不包含该元素,则不发生变。 + * 更正式地讲,删除索引i最低的元素,使得 (o==null ? get(i)==null : o.equals(get(i)))(如果存在这样的元素)。 + * 如果此列表包含指定的元素(或者等效地,如果此列表由于调用而更改),则返回true。 + * + * @param o element to be removed from this list, if present + * @return true 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; + } + + /* + * Private remove method that skips bounds checking and does not + * return the value removed. + * 私有的删除方法,此方法不进行边界检查,也不返回任何值 + */ + private void fastRemove(int index) { + //结构性修改计数 + modCount++; + //一下代码同 E remove(int index) 方法一样 + 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 + } + + /** + * Removes all of the elements from this list. The list will + * be empty after this call returns. + * + * 从此列表中删除所有元素。 该调用返回后,该列表将为空。 + */ + public void clear() { + //结构性修改计数 + modCount++; + + // clear to let GC do its work + // 循环将所有值置空 + for (int i = 0; i < size; i++) + elementData[i] = null; + + //重置 size 为 0 + size = 0; + } + + /** + * Appends all of the elements in the specified collection to the end of + * this list, in the order that they are returned by the + * specified collection's Iterator. The behavior of this operation is + * undefined if the specified collection is modified while the operation + * is in progress. (This implies that the behavior of this call is + * undefined if the specified collection is this list, and this + * list is nonempty.) + * + * 按照指定集合的 Iterator 返回的顺序,将指定集合中的所有元素追加到此列表的末尾。 + * 如果在操作进行过程中修改了指定的集合,则此操作的行为是不确定的。(这意味着如果指定的集合是此列表,并且此列表是非空的,则此调用的行为是不确定的) + * 参数为空则抛出异常。 + * + * @param c collection containing elements to be added to this list + * @return true if this list changed as a result of the call + * @throws NullPointerException if the specified collection is null + */ + public boolean addAll(Collection c) { + //将参数集合转为数组 + Object[] a = c.toArray(); + int numNew = a.length; + //扩容操作 + ensureCapacityInternal(size + numNew); // Increments modCount + //将参数集合中的元素复追加到 elementData 末尾 + System.arraycopy(a, 0, elementData, size, numNew); + size += numNew; + return numNew != 0; + } + + /** + * Inserts all of the elements in the specified collection into this + * list, starting at the specified position. Shifts the element + * currently at that position (if any) and any subsequent elements to + * the right (increases their indices). The new elements will appear + * in the list in the order that they are returned by the + * specified collection's iterator. + * + * 从指定位置开始,将指定集合中的所有元素插入此列表。将当前在该位置的元素(如果有)和任何后续元素右移(增加其索引)。 + * 新元素将按照指定集合的迭代器返回的顺序显示在列表中。 + * 如果指定集合为空将抛出异常 + * + * @param index index at which to insert the first element from the + * specified collection + * @param c collection containing elements to be added to this list + * @return true if this list changed as a result of the call + * @throws IndexOutOfBoundsException {@inheritDoc} + * @throws NullPointerException if the specified collection is null + */ + public boolean addAll(int index, Collection c) { + //边界检查 + rangeCheckForAdd(index); + + //转数组 + Object[] a = c.toArray(); + int numNew = a.length; + //扩容 + ensureCapacityInternal(size + numNew); // Increments modCount + + //计算需要移动的元素数量 + int numMoved = size - index; + if (numMoved > 0) + //将 index 及其后续元素,向右移动 numNew 个单位 + System.arraycopy(elementData, index, elementData, index + numNew, + numMoved); + + //插入参数集合中的所有元素 + System.arraycopy(a, 0, elementData, index, numNew); + size += numNew; + return numNew != 0; + } + + /** + * 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.) + * + * 从此列表中删除索引在 fromIndex(包括)和 toIndex(不包括)之间的所有元素。 将所有后续元素向左移动(减少其索引)。 + * 此调用通过(toIndex-fromIndex)元素来缩短列表。(如果toIndex == fromIndex,则此操作无效。) + * + * @throws IndexOutOfBoundsException if {@code fromIndex} or + * {@code toIndex} is out of range + * ({@code fromIndex < 0 || + * fromIndex >= size() || + * toIndex > size() || + * toIndex < fromIndex}) + */ + protected void removeRange(int fromIndex, int toIndex) { + //结构性修改计数 + modCount++; + //需要移动的元素的数量 + int numMoved = size - toIndex; + //将索引为 toIndex 及其后续元素,向左移动到 fromIndex 的位置。 + System.arraycopy(elementData, toIndex, elementData, fromIndex, + numMoved); + + // clear to let GC do its work + //计算范围删除后,列表的大小 + int newSize = size - (toIndex-fromIndex); + //由于通过采用左移覆盖的方法进行删除,所以需要将末尾的 size - newSize 个元素置空 + for (int i = newSize; i < size; i++) { + elementData[i] = null; + } + size = newSize; + } + + /** + * 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)); + } + + /** + * A version of rangeCheck used by add and addAll. + */ + //边界检查私有方法 + private void rangeCheckForAdd(int index) { + if (index > size || index < 0) + throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); + } + + /** + * Constructs an IndexOutOfBoundsException detail message. + * Of the many possible refactorings of the error handling code, + * this "outlining" performs best with both server and client VMs. + */ + private String outOfBoundsMsg(int index) { + return "Index: "+index+", Size: "+size; + } + + /** + * Removes from this list all of its elements that are contained in the + * specified collection. + * + * 从此列表中删除指定集合中包含的所有其元素。 + * 如果指定集合为空则抛出异常。 + * + * @param c collection containing elements to be removed from this list + * @return {@code true} if this list changed as a result of the call + * @throws ClassCastException if the class of an element of this list + * is incompatible with the specified collection + * (optional) + * @throws NullPointerException if this list contains a null element and the + * specified collection does not permit null elements + * (optional), + * or if the specified collection is null + * @see Collection#contains(Object) + */ + public boolean removeAll(Collection c) { + //集合判空 + Objects.requireNonNull(c); + //批量删除函数 + return batchRemove(c, false); + } + + /** + * Retains only the elements in this list that are contained in the + * specified collection. In other words, removes from this list all + * of its elements that are not contained in the specified collection. + * + * 仅保留此列表中包含在指定集合中的元素。 换句话说,从该列表中删除所有未包含在指定集合中的元素。 + * + * @param c collection containing elements to be retained in this list + * @return {@code true} if this list changed as a result of the call + * @throws ClassCastException if the class of an element of this list + * is incompatible with the specified collection + * (optional) + * @throws NullPointerException if this list contains a null element and the + * specified collection does not permit null elements + * (optional), + * or if the specified collection is null + * @see Collection#contains(Object) + */ + public boolean retainAll(Collection c) { + //集合判空 + Objects.requireNonNull(c); + //批量删除函数 + return batchRemove(c, true); + } + + //批量删除函数。complement参数: + //true -> 只保留 elementData 中, elementData 与集合 c 的交集部分 + //false -> 删除 elementData 中, elementData 与集合 c 的交集部分 + private boolean batchRemove(Collection c, boolean complement) { + final Object[] elementData = this.elementData; + //采用左右指针的方式,r 为右指针,用于遍历整个 elementData,w 为左指针,用于索引留下的元素 + int r = 0, w = 0; + //记录 elementData 是否被修改 + boolean modified = false; + try { + for (; r < size; r++) + //如果 complement 为 true,就是保留 c 包含 elementData 的部分。否则就保留不包含的部分。 + if (c.contains(elementData[r]) == complement) + //每当记录一个元素后,w 左指针 ++ + elementData[w++] = elementData[r]; + } finally { + // Preserve behavioral compatibility with AbstractCollection, + // even if c.contains() throws. + // 即使 c.contains() 抛出异常,仍保留与AbstractCollection的行为兼容性。 + // c.contains() 很有可能会抛出异常,如果抛出异常就不能保证数据的完整性。 + // 所以这里判断,当右指针没有遍历到循环的结尾,说明发生异常退出循环了,那么剩下的未循环的部分,需要追加到已经判断完的元素的后面,以保证数据的完整性。 + if (r != size) { + // 将右指针 r 及其后续所有元素,左移到左指针 w 处 + System.arraycopy(elementData, r, + elementData, w, + size - r); + w += size - r; + } + // 当左指针 w 不等于 size 时,说明有些元素被排除了,也就是说,列表发生了改变。 + if (w != size) { + // clear to let GC do its work + // 将列表末尾那些排除的元素置空 + for (int i = w; i < size; i++) + elementData[i] = null; + // 结构性修改计数,这里不是++,而是增加了列表修改的长度 + modCount += size - w; + size = w; + // 设置当前列表已被修改 + modified = true; + } + } + return modified; + } + + /** + * Save the state of the ArrayList instance to a stream (that + * is, serialize it). + * + * 保存 ArrayList 实例的状态到一个流中,也就是序列化实例。 + * + * @serialData The length of the array backing the ArrayList + * instance 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 element count, and any hidden stuff + //在序列化期间,列表不能被结构性修改,所以这里记录序列化开始时的 modCount,并在序列化结束后验证是否与开始时记录的 modCount 相同,如果不同则会抛出异常 + 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; iArrayList instance from a stream (that is, + * 从一个流中重构 ArrayList 实例,也就是反序列化 + * 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; iThe returned list iterator is fail-fast. + * + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public ListIterator listIterator(int index) { + //边界判断 + if (index < 0 || index > size) + throw new IndexOutOfBoundsException("Index: "+index); + //返回迭代器实例 ListItr + return new ListItr(index); + } + + /** + * Returns a list iterator over the elements in this list (in proper + * sequence). + * + * 返回此列表中的元素的列表迭代器(按适当顺序)。返回的迭代器是快速失败的。 + * + *

The returned list iterator is fail-fast. + * + * @see #listIterator(int) + */ + public ListIterator listIterator() { + //返回迭代器实例 ListItr + return new ListItr(0); + } + + /** + * Returns an iterator over the elements in this list in proper sequence. + * + *

The returned iterator is fail-fast. + * + * 以正确的顺序返回此列表中元素的迭代器。返回的迭代器是快速失败的。 + * + * @return an iterator over the elements in this list in proper sequence + */ + public Iterator iterator() { + //返回迭代器实例 Itr + return new Itr(); + } + + /** + * An optimized version of AbstractList.Itr + * AbstractList.Itr 的优化版本 + */ + // 注意,在迭代器迭代的过程中,列表的结构是不能修改的,否则会引发 ConcurrentModificationException 异常。 + // 如果需要在迭代期间修改列表结构,只能使用迭代器自己的方法,而不能是 ArrayList 实例的方法。 + private class Itr implements Iterator { + int cursor; // index of next element to return 下一个需要返回的元素的游标索引,初始值为 0 + int lastRet = -1; // index of last element returned; -1 if no such 最后一次操作返回的元素的索引,初始值 -1 + int expectedModCount = modCount; //用于结构性修改的判断 + + Itr() {} + + //判断是否存在下一个元素 + public boolean hasNext() { + return cursor != size; + } + + //返回迭代中的下一个元素。 + @SuppressWarnings("unchecked") + public E next() { + //结构性修改判断 + checkForComodification(); + int i = cursor; + //如果下一个元素游标索引大于 size,则抛出异常 + if (i >= size) + throw new NoSuchElementException(); + //获取 ArrayList 实例的 elementData + Object[] elementData = ArrayList.this.elementData; + //这里对 length 的判断,是尽最大可能性的保证迭代器的快速失败。 + //当并发调用的不确定性情况下,有可能修改了 elementData,但 expectedModCount = modCount 的情况发生,所以仍需要对 length 进行一层判断。 + if (i >= elementData.length) + throw new ConcurrentModificationException(); + //游标后移一位 + cursor = i + 1; + //返回当前元素 + return (E) elementData[lastRet = i]; + } + + // 从基础集合中移除此迭代器返回的最后一个元素(可选操作)。每次调用 next() 方法后,只能调用一次此方法。 + // 如果在迭代进行过程中以其他方式(而不是通过调用此方法)修改了基础集合,则迭代器的行为是不确定的。 + public void remove() { + //每次执行删除方法后,都会重置 lastRet 为 -1,以防止本方法被多次调用。以及不能一上来就 remove,需要先 next + if (lastRet < 0) + throw new IllegalStateException(); + //结构性修改检查 + checkForComodification(); + + try { + //调用 ArrayList 的 remove 实现 + ArrayList.this.remove(lastRet); + //删除后,下一个元素游标 cursor 指向最后一次操作索引 lastRet。 + //因为删除操作,会将剩余元素左移,所以下一个待返回的元素的游标索引就是当前删除位置的索引。 + cursor = lastRet; + //重置 lastRet + lastRet = -1; + //修改结构性修改的计数 + expectedModCount = modCount; + } catch (IndexOutOfBoundsException ex) { + throw new ConcurrentModificationException(); + } + } + + //对剩余的每个元素执行给定的操作,直到所有元素都已处理或该操作引发异常。 + //如果指定了顺序,则按照迭代顺序执行操作。 该操作引发的异常将中继到调用方。 + //参数为空则抛出异常 + //函数式调用,lamaba 表达式 + @Override + @SuppressWarnings("unchecked") + public void forEachRemaining(Consumer consumer) { + //参数判空 + Objects.requireNonNull(consumer); + //当前列表元素的数量 + final int size = ArrayList.this.size; + int i = cursor; + //如果游标超出元素数量,则不进行任何操作 + if (i >= size) { + return; + } + final Object[] elementData = ArrayList.this.elementData; + if (i >= elementData.length) { + throw new ConcurrentModificationException(); + } + //当游标值不等于 size ,并且没有结构性修改时,则进行循环遍历 + while (i != size && modCount == expectedModCount) { + //将每一个元素用于 lamaba 表达式 + consumer.accept((E) elementData[i++]); + } + // update once at end of iteration to reduce heap write traffic + //如果循环顺利完成,则一次性修改游标及lastRet的值 + cursor = i; + lastRet = i - 1; + //最后在检查一次是否结构性修改 + checkForComodification(); + } + + //结构性修改检查 + final void checkForComodification() { + if (modCount != expectedModCount) + throw new ConcurrentModificationException(); + } + } + + /** + * An optimized version of AbstractList.ListItr + * AbstractList.ListItr 的改进版 + */ + // ListIterator : + // 列表迭代器,允许程序员在任一方向上遍历列表,在迭代过程中修改列表,并获取迭代器在列表中的当前位置。 + // ListIterator没有当前元素。 它的光标位置始终位于通过调用 previous() 返回的元素和通过调用 next() 返回的元素之间。 + // 长度为n的列表的迭代器具有 n + 1 个可能的光标位置,如下面的插入符号(^)所示: + // Element(0) Element(1) Element(2) ... Element(n-1) + //cursor positions: ^ ^ ^ ^ ^ + //注意,remove() 和 set(Object) 方法不是根据光标位置定义的,它们被定义为对调用 next() 或 previous() 返回的最后一个元素进行操作。 + // + private class ListItr extends Itr implements ListIterator { + //迭代器可以选择遍历的起始位置,而这个位置也是 cursor 游标的初始值 + ListItr(int index) { + super(); + cursor = index; + } + + //是否有前一个元素 + public boolean hasPrevious() { + return cursor != 0; + } + + //是否有下一个元素 + public int nextIndex() { + return cursor; + } + + //前一个游标的索引,注意不是元素的索引。 + public int previousIndex() { + return cursor - 1; + } + + // 返回列表中的上一个元素,并将光标位置向后移动。 + // 可以重复调用此方法以向后遍历列表,也可以将其与对 next() 的调用来回混合。 + // 请注意,交替调用 next 和 Previous 将重复返回相同的元素。即 next -> previous -> next 返回相同的元素 + @SuppressWarnings("unchecked") + public E previous() { + //结构性修改检查 + checkForComodification(); + //获取上一个游标索引 + int i = cursor - 1; + //游标边界检查 + if (i < 0) + throw new NoSuchElementException(); + Object[] elementData = ArrayList.this.elementData; + if (i >= elementData.length) + throw new ConcurrentModificationException(); + //将游标后移一位(也就是左移一位) + cursor = i; + //返回元素 + return (E) elementData[lastRet = i]; + } + + // 将 next() 或 previous() 返回的最后一个元素替换为指定的元素(可选操作)。 + // 仅在最后一次调用 next 或 last 之后没有调用 remove() 和 add(E) 时才能进行此调用。 + public void set(E e) { + //同上述注解中描述,remove() 和 add(E) 方法调用后,会重置 lastRet 为 -1 + if (lastRet < 0) + throw new IllegalStateException(); + //结构性修改检查 + checkForComodification(); + + try { + //调用 ArrayList 实例的 set 实现 + ArrayList.this.set(lastRet, e); + } catch (IndexOutOfBoundsException ex) { + throw new ConcurrentModificationException(); + } + } + + // 将指定的元素插入列表(可选操作)。 + // 新元素将插入到 next() 方法返回的元素之前(如果有的话)与 previous() 方法返回的元素之后(如果有的话) + // 如果列表不包含任何元素,则新元素将成为列表上唯一的元素。 + // 新元素将插入到隐式光标之前 :对 next 的后续调用将不受影响,而对 Previous 的后续调用将返回新元素。 + // 此调用将 nextIndex 或 previousIndex 的调用返回的值增加一个。 + public void add(E e) { + //结构性修改检查 + checkForComodification(); + + try { + int i = cursor; + //调用 ArrayList 的 add 实现,将新元素插入到当前游标的位置 + ArrayList.this.add(i, e); + //当前游标向前移动一位,也就是右移一位。 + //对 next 的后续调用将不受影响,而对 Previous 的后续调用将返回新元素。 + cursor = i + 1; + //重置 lastRet + lastRet = -1; + //修改结构性修改的计数 + expectedModCount = modCount; + } catch (IndexOutOfBoundsException ex) { + throw new ConcurrentModificationException(); + } + } + } + + /** + * Returns a view of the portion of this list between the specified + * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive. (If + * {@code fromIndex} and {@code toIndex} are equal, the returned list is + * empty.) The returned list is backed by this list, so non-structural + * changes in the returned list are reflected in this list, and vice-versa. + * The returned list supports all of the optional list operations. + * + *

This method eliminates the need for explicit range operations (of + * the sort that commonly exist for arrays). Any operation that expects + * a list can be used as a range operation by passing a subList view + * instead of a whole list. For example, the following idiom + * removes a range of elements from a list: + *

+     *      list.subList(from, to).clear();
+     * 
+ * Similar idioms may be constructed for {@link #indexOf(Object)} and + * {@link #lastIndexOf(Object)}, and all of the algorithms in the + * {@link Collections} class can be applied to a subList. + * + *

The semantics of the list returned by this method become undefined if + * the backing list (i.e., this list) is structurally modified in + * any way other than via the returned list. (Structural modifications are + * those that change the size of this list, or otherwise perturb it in such + * a fashion that iterations in progress may yield incorrect results.) + * + * @throws IndexOutOfBoundsException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + * + * 返回此列表中指定的 fromIndex(包括) 和 toIndex(不包括) 之间的视图。如果fromIndex 和 toIndex 相等,则返回的列表为空。 + * 此列表支持返回的列表,因此返回的列表中的非结构性更改会反映在此列表中,反之亦然。 返回的列表支持所有可选列表操作。 + * + * 此方法消除了对显式范围操作(数组通常存在的那种范围)的需要。 + * 通过传递subList视图而不是整个列表,可以将期望列表的任何操作用作范围操作。 例如,以下示例从列表中删除了一系列元素: + * + * list.subList(from, to).clear(); + * + * 可以为 indexOf(Object) 和 lastIndexOf(Object) 构造类似的习惯用法,并且 Collections 类中的所有算法都可以应用于 subList。 + * + * 如果后备列表(即此列表)以结构方式(而不是通过返回的列表)进行了修改,则此方法返回的列表的语义将变得不确定。 + * 结构修改是指更改此列表的大小的结构修改,或者以其他方式干扰此列表的方式,即正在进行的迭代可能会产生错误的结果。 + * + */ + public List subList(int fromIndex, int toIndex) { + //边界检查 + subListRangeCheck(fromIndex, toIndex, size); + //返回子列表实例 SubList + return new SubList(this, 0, fromIndex, toIndex); + } + + //边界检查 + static void subListRangeCheck(int fromIndex, int toIndex, int size) { + if (fromIndex < 0) + throw new IndexOutOfBoundsException("fromIndex = " + fromIndex); + if (toIndex > size) + throw new IndexOutOfBoundsException("toIndex = " + toIndex); + if (fromIndex > toIndex) + throw new IllegalArgumentException("fromIndex(" + fromIndex + + ") > toIndex(" + toIndex + ")"); + } + + //SubList 实现部分 + //继承自AbstractList,其中有 modCount 成员变量。 + //SubList 又重写了一遍 ArrayList 的一些方法,这说明如果想要修改子列表的结构,只能使用子列表提供的方法,而不是 ArrayList 的 + private class SubList extends AbstractList implements RandomAccess { + //父列表,这里不一定是最原始的列表,也会是父-子列表(子列表一样可以再有子列表) + private final AbstractList parent; + //父列表的偏移量 + private final int parentOffset; + //总偏移量,用于原始列表 + private final int offset; + //当前 SubList 的元素数量 + int size; + + SubList(AbstractList parent, + int offset, int fromIndex, int toIndex) { + this.parent = parent; + this.parentOffset = fromIndex; + this.offset = offset + fromIndex; + this.size = toIndex - fromIndex; + this.modCount = ArrayList.this.modCount; //同步原始列表结构性修改计数 modCount + } + + public E set(int index, E e) { + //边界检查 + rangeCheck(index); + //结构性修改检查 + checkForComodification(); + //获取原始列表中,总偏移量处的旧值 + E oldValue = ArrayList.this.elementData(offset + index); + //将原始列表中,总偏移量的位置设置新值 + ArrayList.this.elementData[offset + index] = e; + //返回旧值 + return oldValue; + } + + public E get(int index) { + //边界检查 + rangeCheck(index); + //结构性修改检查 + checkForComodification(); + //获取原始列表中,总偏移量处的旧值 + return ArrayList.this.elementData(offset + index); + } + + public int size() { + //结构性修改检查 + checkForComodification(); + return this.size; + } + + public void add(int index, E e) { + //边界检查 + rangeCheckForAdd(index); + //结构性修改检查 + checkForComodification(); + //使用父列表的 add 实现,所以使用父偏移量 + //这里需要说明,为什么使用了父列表的方法,而不是像上面 set 方法使用原始列表呢?我的想法是: + //主要原因在 modCount,因为子列表也可以在产生子列表,当最后一层子列表进行 add 调用时, + //其实类似于递归的回溯操作,需要在每一层都去判断 modCount,只有完整的回溯到原始列表,才能保证其结构性的一致性。 + //如果无论哪一层抛出异常,则会抛出异常从而保证其快速失败。 + parent.add(parentOffset + index, e); + //同步父列表的 modCount + this.modCount = parent.modCount; + this.size++; + } + + public E remove(int index) { + //边界检查 + rangeCheck(index); + //结构性修改检查 + checkForComodification(); + //调用父列表的删除实现 + E result = parent.remove(parentOffset + index); + //同步父列表的 modCount + this.modCount = parent.modCount; + this.size--; + return result; + } + + protected void removeRange(int fromIndex, int toIndex) { + //结构性修改检查 + checkForComodification(); + //调用父列表的 removeRange 实现 + parent.removeRange(parentOffset + fromIndex, + parentOffset + toIndex); + //同步父列表的 modCount + this.modCount = parent.modCount; + this.size -= toIndex - fromIndex; + } + + public boolean addAll(Collection c) { + return addAll(this.size, c); + } + + public boolean addAll(int index, Collection c) { + //边界检查 + rangeCheckForAdd(index); + int cSize = c.size(); + //如果参数集合没有元素,则返回 false + if (cSize==0) + return false; + + //结构性修改检查 + checkForComodification(); + //调用父列表的 addAll 实现 + parent.addAll(parentOffset + index, c); + //同步父列表的 modCount + this.modCount = parent.modCount; + this.size += cSize; + return true; + } + + public Iterator iterator() { + return listIterator(); + } + + //使用匿名内部类,重写了一遍迭代器的实现 + public ListIterator listIterator(final int index) { + //结构性修改检查 + checkForComodification(); + //边界检查 + rangeCheckForAdd(index); + final int offset = this.offset; + + //大部分代码的含义同 ArrayList 的迭代器相同 + return new ListIterator() { + int cursor = index; + int lastRet = -1; + //获取原始列表的 modCount,用于后续结构性修改判断 + int expectedModCount = ArrayList.this.modCount; + + public boolean hasNext() { + return cursor != SubList.this.size; + } + + //除了边界条件有所不同,剩余代码均相同。可以往回参考 + @SuppressWarnings("unchecked") + public E next() { + checkForComodification(); + int i = cursor; + if (i >= SubList.this.size) + throw new NoSuchElementException(); + Object[] elementData = ArrayList.this.elementData; + //这里由于使用的是原始列表,所以使用 offset 总偏移量 + if (offset + i >= elementData.length) + throw new ConcurrentModificationException(); + cursor = i + 1; + return (E) elementData[offset + (lastRet = i)]; + } + + public boolean hasPrevious() { + return cursor != 0; + } + + //除了边界条件有所不同,剩余代码均相同。可以往回参考 + @SuppressWarnings("unchecked") + public E previous() { + checkForComodification(); + int i = cursor - 1; + if (i < 0) + throw new NoSuchElementException(); + Object[] elementData = ArrayList.this.elementData; + if (offset + i >= elementData.length) + throw new ConcurrentModificationException(); + cursor = i; + return (E) elementData[offset + (lastRet = i)]; + } + + //除了边界条件有所不同,剩余代码均相同。可以往回参考 + @SuppressWarnings("unchecked") + public void forEachRemaining(Consumer consumer) { + Objects.requireNonNull(consumer); + final int size = SubList.this.size; + int i = cursor; + if (i >= size) { + return; + } + final Object[] elementData = ArrayList.this.elementData; + if (offset + i >= elementData.length) { + throw new ConcurrentModificationException(); + } + while (i != size && modCount == expectedModCount) { + consumer.accept((E) elementData[offset + (i++)]); + } + // update once at end of iteration to reduce heap write traffic + lastRet = cursor = i; + checkForComodification(); + } + + public int nextIndex() { + return cursor; + } + + public int previousIndex() { + return cursor - 1; + } + + //除了边界条件有所不同,剩余代码均相同。可以往回参考 + public void remove() { + if (lastRet < 0) + throw new IllegalStateException(); + checkForComodification(); + + try { + //结构性修改,使用当前子列表 + SubList.this.remove(lastRet); + cursor = lastRet; + lastRet = -1; + expectedModCount = ArrayList.this.modCount; + } catch (IndexOutOfBoundsException ex) { + throw new ConcurrentModificationException(); + } + } + + //除了边界条件有所不同,剩余代码均相同。可以往回参考 + public void set(E e) { + if (lastRet < 0) + throw new IllegalStateException(); + checkForComodification(); + + try { + //非结构性修改,使用原始列表 + ArrayList.this.set(offset + lastRet, e); + } catch (IndexOutOfBoundsException ex) { + throw new ConcurrentModificationException(); + } + } + + //除了边界条件有所不同,剩余代码均相同。可以往回参考 + public void add(E e) { + checkForComodification(); + + try { + int i = cursor; + //结构性修改,使用当前子列表 + SubList.this.add(i, e); + cursor = i + 1; + lastRet = -1; + expectedModCount = ArrayList.this.modCount; + } catch (IndexOutOfBoundsException ex) { + throw new ConcurrentModificationException(); + } + } + + //结构性修改检查 + final void checkForComodification() { + if (expectedModCount != ArrayList.this.modCount) + throw new ConcurrentModificationException(); + } + }; + } + + //这里就是子列表可以再生成子列表 + public List subList(int fromIndex, int toIndex) { + //边界检查 + subListRangeCheck(fromIndex, toIndex, size); + return new SubList(this, offset, fromIndex, toIndex); + } + + //边界检查 + private void rangeCheck(int index) { + if (index < 0 || index >= this.size) + throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); + } + + //边界检查 + private void rangeCheckForAdd(int index) { + if (index < 0 || index > this.size) + throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); + } + + private String outOfBoundsMsg(int index) { + return "Index: "+index+", Size: "+this.size; + } + + //结构性修改检查 + private void checkForComodification() { + if (ArrayList.this.modCount != this.modCount) + throw new ConcurrentModificationException(); + } + + //TODO() Spliterator 暂时还未分析...... + public Spliterator spliterator() { + checkForComodification(); + return new ArrayListSpliterator(ArrayList.this, offset, + offset + this.size, this.modCount); + } + } + + //1.8 的语法糖,用于 lamaba 表达式 + @Override + public void forEach(Consumer action) { + //参数判空 + Objects.requireNonNull(action); + //获取 modCount + final int expectedModCount = modCount; + @SuppressWarnings("unchecked") + final E[] elementData = (E[]) this.elementData; + final int size = this.size; + //将每一个元素传入函数式参数。每次循环都会判断 modCount + for (int i=0; modCount == expectedModCount && i < size; i++) { + action.accept(elementData[i]); + } + //最后在判断一次 modCount + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + } + + //TODO() + /** + * Creates a late-binding + * and fail-fast {@link Spliterator} over the elements in this + * list. + * + *

The {@code Spliterator} reports {@link Spliterator#SIZED}, + * {@link Spliterator#SUBSIZED}, and {@link Spliterator#ORDERED}. + * Overriding implementations should document the reporting of additional + * characteristic values. + * + * @return a {@code Spliterator} over the elements in this list + * @since 1.8 + */ + @Override + public Spliterator spliterator() { + return new ArrayListSpliterator<>(this, 0, -1, 0); + } + + //TODO() + /** Index-based split-by-two, lazily initialized Spliterator */ + static final class ArrayListSpliterator implements Spliterator { + + /* + * If ArrayLists were immutable, or structurally immutable (no + * adds, removes, etc), we could implement their spliterators + * with Arrays.spliterator. Instead we detect as much + * interference during traversal as practical without + * sacrificing much performance. We rely primarily on + * modCounts. These are not guaranteed to detect concurrency + * violations, and are sometimes overly conservative about + * within-thread interference, but detect enough problems to + * be worthwhile in practice. To carry this out, we (1) lazily + * initialize fence and expectedModCount until the latest + * point that we need to commit to the state we are checking + * against; thus improving precision. (This doesn't apply to + * SubLists, that create spliterators with current non-lazy + * values). (2) We perform only a single + * ConcurrentModificationException check at the end of forEach + * (the most performance-sensitive method). When using forEach + * (as opposed to iterators), we can normally only detect + * interference after actions, not before. Further + * CME-triggering checks apply to all other possible + * violations of assumptions for example null or too-small + * elementData array given its size(), that could only have + * occurred due to interference. This allows the inner loop + * of forEach to run without any further checks, and + * simplifies lambda-resolution. While this does entail a + * number of checks, note that in the common case of + * list.stream().forEach(a), no checks or other computation + * occur anywhere other than inside forEach itself. The other + * less-often-used methods cannot take advantage of most of + * these streamlinings. + */ + + private final ArrayList list; + private int index; // current index, modified on advance/split + private int fence; // -1 until used; then one past last index + private int expectedModCount; // initialized when fence set + + /** Create new spliterator covering the given range */ + ArrayListSpliterator(ArrayList list, int origin, int fence, + int expectedModCount) { + this.list = list; // OK if null unless traversed + this.index = origin; + this.fence = fence; + this.expectedModCount = expectedModCount; + } + + private int getFence() { // initialize fence to size on first use + int hi; // (a specialized variant appears in method forEach) + ArrayList lst; + if ((hi = fence) < 0) { + if ((lst = list) == null) + hi = fence = 0; + else { + expectedModCount = lst.modCount; + hi = fence = lst.size; + } + } + return hi; + } + + public ArrayListSpliterator trySplit() { + int hi = getFence(), lo = index, mid = (lo + hi) >>> 1; + return (lo >= mid) ? null : // divide range in half unless too small + new ArrayListSpliterator(list, lo, index = mid, + expectedModCount); + } + + public boolean tryAdvance(Consumer action) { + if (action == null) + throw new NullPointerException(); + int hi = getFence(), i = index; + if (i < hi) { + index = i + 1; + @SuppressWarnings("unchecked") E e = (E)list.elementData[i]; + action.accept(e); + if (list.modCount != expectedModCount) + throw new ConcurrentModificationException(); + return true; + } + return false; + } + + public void forEachRemaining(Consumer action) { + int i, hi, mc; // hoist accesses and checks from loop + ArrayList lst; Object[] a; + if (action == null) + throw new NullPointerException(); + if ((lst = list) != null && (a = lst.elementData) != null) { + if ((hi = fence) < 0) { + mc = lst.modCount; + hi = lst.size; + } + else + mc = expectedModCount; + if ((i = index) >= 0 && (index = hi) <= a.length) { + for (; i < hi; ++i) { + @SuppressWarnings("unchecked") E e = (E) a[i]; + action.accept(e); + } + if (lst.modCount == mc) + return; + } + } + throw new ConcurrentModificationException(); + } + + public long estimateSize() { + return (long) (getFence() - index); + } + + public int characteristics() { + return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED; + } + } + + // 删除此集合中满足给定谓词的所有元素。 在迭代过程中或谓词中引发的错误或运行时异常将中继给调用方。 + // 用于 lamaba 表达式 + @Override + public boolean removeIf(Predicate filter) { + //参数判空 + Objects.requireNonNull(filter); + // figure out which elements are to be removed + // any exception thrown from the filter predicate at this stage + // will leave the collection unmodified + // 找到需要删除的元素。如果在此期间抛出任何异常,将不会对列表有任何的改动 + int removeCount = 0; //需要删除元素的数量 + //BitSet是一个位操作 set,所有位默认值 false,添加到其中的值的位置会被设置为 true。 + final BitSet removeSet = new BitSet(size); + //设置modCount + final int expectedModCount = modCount; + final int size = this.size; + //循环遍历,找出所有需要删除的元素,每次遍历都会判断 modCount + for (int i=0; modCount == expectedModCount && i < size; i++) { + @SuppressWarnings("unchecked") + final E element = (E) elementData[i]; + //通过函数式编程接口 Predicate 的 test 方法,找出需要删除的元素 + if (filter.test(element)) { + //如果找到,则设置 BitSet 对应位置为 true + removeSet.set(i); + //增加计数 + removeCount++; + } + } + //判断modCount + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + + // shift surviving elements left over the spaces left by removed elements + final boolean anyToRemove = removeCount > 0; + //如果有需要删除的元素,则通过将剩余元素左移来填补空位的方式,删除元素 + if (anyToRemove) { + //删除元素后的列表大小 + final int newSize = size - removeCount; + //同样采用了左右指针的方式,保留 BitSet 中的 false 位 + for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) { + i = removeSet.nextClearBit(i); //返回下一个 false 位 + elementData[j] = elementData[i]; + } + //左移之后,将新大小列表的剩余元素置空 + for (int k=newSize; k < size; k++) { + elementData[k] = null; // Let gc do its work + } + this.size = newSize; + //最后在判断一次 modCount + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + //结构性修改计数增加 + modCount++; + } + + //返回是否有元素被删除了 + return anyToRemove; + } + + // 使用基于 operator 对此列表每个元素计算后的结果,替换此列表中的每个元素。 + // 用于 lamaba 表达式 + @Override + @SuppressWarnings("unchecked") + // 代码基本相似,可往回参考 + public void replaceAll(UnaryOperator operator) { + Objects.requireNonNull(operator); + final int expectedModCount = modCount; + final int size = this.size; + for (int i=0; modCount == expectedModCount && i < size; i++) { + elementData[i] = operator.apply((E) elementData[i]); + } + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + modCount++; + } + + //根据指定的比较器的顺序对该列表进行排序。 + //使用指定的比较器,此列表中的所有元素必须可以相互比较。即,c.compare(e1,e2) 不得对列表中的任何元素 e1 和 e2 抛出 ClassCastException。 + //如果指定的比较器为null,则此列表中的所有元素都必须实现 Comparable 接口,并且应使用元素的自然顺序。 + //该列表必须是可修改的,但无需调整大小。 + @Override + @SuppressWarnings("unchecked") + public void sort(Comparator c) { + final int expectedModCount = modCount; + Arrays.sort((E[]) elementData, 0, size, c); + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + modCount++; + } +} +``` \ No newline at end of file -- Gitee From 34acb5885b4730d18f3f93e811f3b7d5f6f28ae7 Mon Sep 17 00:00:00 2001 From: RDCynthia Date: Wed, 4 Mar 2020 10:33:33 +0800 Subject: [PATCH 2/4] rename file --- second/week_01/81/{ArrayaList.md => ArrayList.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename second/week_01/81/{ArrayaList.md => ArrayList.md} (100%) diff --git a/second/week_01/81/ArrayaList.md b/second/week_01/81/ArrayList.md similarity index 100% rename from second/week_01/81/ArrayaList.md rename to second/week_01/81/ArrayList.md -- Gitee From e3a7825d695beb2421061704325f916f56529974 Mon Sep 17 00:00:00 2001 From: RDCynthia Date: Thu, 5 Mar 2020 20:22:43 +0800 Subject: [PATCH 3/4] Commit LinkedList Homework --- second/week_01/81/LinkedList.md | 1410 +++++++++++++++++++++++++++++++ 1 file changed, 1410 insertions(+) create mode 100644 second/week_01/81/LinkedList.md diff --git a/second/week_01/81/LinkedList.md b/second/week_01/81/LinkedList.md new file mode 100644 index 0000000..b138dc9 --- /dev/null +++ b/second/week_01/81/LinkedList.md @@ -0,0 +1,1410 @@ +#### 简述 + +``` +public class LinkedList + extends AbstractSequentialList + implements List, Deque, Cloneable, java.io.Serializable +``` + +List 和 Deque 接口的双端链表实现。实现所有可选的列表操作,并允许所有元素(包括null)。 + +所有操作都按照双向链表的预期执行。链表中索引的操作从列表的开头或结尾开始遍历列表,以更接近指定索引的位置为准。 + +请注意,此实现不是同步的。如果多个线程同时访问一个链表,并且至少有一个线程在结构上修改了链表,则必须在外部进行同步。结构修改是指添加或删除一个或多个元素的任何操作,仅设置元素的值不是结构修改。通常通过在自然封装链表的某个对象上进行同步来完成此操作。如果不存在这样的对象,则应使用 Collections.synchronizedList 方法“包装”链表。最好在创建时完成此操作,以防止意外的不同步访问链表: + +``` +List list = Collections.synchronizedList(new LinkedList(...)); +``` + +此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的。如果在创建迭代器后的任何时候以任何方式对链表进行结构修改,除非通过迭代器自己的 remove 或 add 方法,否则迭代器将抛出 ConcurrentModificationException。因此,面对并发修改,迭代器将快速而干净地失败,而不是在未来的不确定时间内冒任意,不确定的行为的风险。 + +注意,迭代器的快速失败行为无法得到保证,因为通常来说,在存在不同步的并发修改的情况下,不可能做出任何严格的保证。快速失败的迭代器会尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常而判断正确性的程序是错误的 : 迭代器的快速失败行为应仅用于检测错误。 + +#### 源码分析 + +LinkedList 的访问与搜索时间复杂度是 O(n)的,插入与删除是 O(1)的。其源码所有的增删改查都基于或调用了内部共用的几个方法 : + +`linkFirst`(在头部插入一个结点)、`linkLast`(在尾部插入一个结点)、`linkBefore`(在特定元素前插入一个结点)、`unlinkFirst`(删除头结点)、`unlinkLast`(删除尾结点)、`unlink`(删除一个指定的结点) + +看懂了这几个方法,其他所有 API 方法几乎都差不多,无外乎有的抛出异常,有的不抛出,有的移除并返回,有的不移除直接返回,甚至例如 add、addLast、offer、offerLast 等他们完全是一样的。 + +```java +package java.util; + +import java.util.function.Consumer; + +public class LinkedList + extends AbstractSequentialList + implements List, Deque, Cloneable, java.io.Serializable +{ + //链表的大小,也是链表中元素的数量 + transient int size = 0; + + /** + * Pointer to first node. + * + * 指向第一个节点 + * + * Invariant: (first == null && last == null) || + * (first.prev == null && first.item != null) + */ + //头结点 + transient Node first; + + /** + * Pointer to last node. + * + * 指向最后一个节点 + * + * Invariant: (first == null && last == null) || + * (last.next == null && last.item != null) + */ + //尾结点 + transient Node last; + + /** + * Constructs an empty list. + */ + public LinkedList() { + } + + /** + * Constructs a list containing the elements of the specified + * collection, in the order they are returned by the collection's + * iterator. + * + * 构造一个包含指定集合的元素的列表,其顺序由集合的迭代器返回。 + * + * @param c the collection whose elements are to be placed into this list + * @throws NullPointerException if the specified collection is null + */ + public LinkedList(Collection c) { + this(); + //调用 addAll 方法,添加元素集合 + addAll(c); + } + + /** + * Links e as first element. + * + * 在链表头部插入一个结点 + */ + private void linkFirst(E e) { + //当前的头结点 + final Node f = first; + //创建要插入的新结点,因为在头部插入,所以前指针为 null,后指针指向当前头结点 first + //Node 结点的定义,在最后面有 + final Node newNode = new Node<>(null, e, f); + //设置新结点为头结点 + first = newNode; + //如果 f 为空,表示在添加前,这是一个空链表,那头结点和尾结点就都是这个新结点了 + if (f == null) + last = newNode; + //否则在新头结点与原头结点之间建立双向关联 + else + f.prev = newNode; + size++; + //结构性修改计数 + modCount++; + } + + /** + * Links e as last element. + * + * 在链表尾部添加一个结点 + */ + //代码同 linkFirst(E e) 差不多 + void linkLast(E e) { + final Node l = last; + final Node newNode = new Node<>(l, e, null); + last = newNode; + if (l == null) + first = newNode; + else + l.next = newNode; + size++; + modCount++; + } + + /** + * Inserts element e before non-null Node succ. + * + * 在指定的结点 succ 前插入一个元素 + */ + void linkBefore(E e, Node succ) { + // assert succ != null; 假设已经判断 succ 不为空 + //获取指定结点的前一个结点 + final Node pred = succ.prev; + //创建待插入的新结点,其前指针是指定结点的前一个结点,后指针就是指定的结点 + final Node newNode = new Node<>(pred, e, succ); + //指定结点的前指针指向新结点 + succ.prev = newNode; + //如果 pred 为空,说明 succ 是头结点,那新结点插入后就会变为头结点 + if (pred == null) + first = newNode; + //否则将 pred 的尾结点指向新结点 + else + pred.next = newNode; + size++; + //结构性修改计数 + modCount++; + } + + /** + * Unlinks non-null first node f. + * + * 移除头结点 + */ + //这个方法虽然参数是传入结点,但后续所有的调用,都是传入的 first 头结点 + private E unlinkFirst(Node f) { + // assert f == first && f != null; + //获取值用于返回 + final E element = f.item; + //获取头结点的下一个结点 + final Node next = f.next; + //将内容置空 + f.item = null; + //后指针置空,用于垃圾回收 + f.next = null; // help GC + //将 next 设置为头结点 + first = next; + //如果 next 为空,说明此时链表为空,则 first = last = null + if (next == null) + last = null; + else + next.prev = null; //否则将 next 前指针置空,因为它已经是头结点了 + size--; + //结构性修改计数 + modCount++; + return element; + } + + /** + * Unlinks non-null last node l. + * + * 移除尾结点 + */ + //代码同 unlinkFirst(Node f) 差不多 + private E unlinkLast(Node l) { + // assert l == last && l != null; + final E element = l.item; + final Node prev = l.prev; + l.item = null; + l.prev = null; // help GC + last = prev; + if (prev == null) + first = null; + else + prev.next = null; + size--; + modCount++; + return element; + } + + /** + * Unlinks non-null node x. + * + * 移除指定结点 + */ + //只需将待移除结点的前结点和后结点建立双向关联即可 + E unlink(Node x) { + // assert x != null; + final E element = x.item; + //获取后结点 + final Node next = x.next; + //获取前结点 + final Node prev = x.prev; + + //下面两组判断,用于四种情况: + //1. x 是链表中唯一的一个结点,删除后 first = last = null + //2. x 是头结点,则删除后,next 为头结点 + //3. x 是尾结点,则删除后,prev 为尾结点 + //4. x 是中间任意结点,则删除后,prev 与 next 建立双向关联即可 + 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; + } + + /** + * Returns the first element in this list. + * + * 返回此列表中的第一个元素。头结点为空抛出异常 + * + * @return the first element in this list + * @throws NoSuchElementException if this list is empty + */ + public E getFirst() { + final Node f = first; + if (f == null) + throw new NoSuchElementException(); + return f.item; + } + + /** + * Returns the last element in this list. + * + * 回此列表中的最后一个元素。尾结点为空抛出异常 + * + * @return the last element in this list + * @throws NoSuchElementException if this list is empty + */ + public E getLast() { + final Node l = last; + if (l == null) + throw new NoSuchElementException(); + return l.item; + } + + /** + * Removes and returns the first element from this list. + * + * 从此列表中删除并返回第一个元素。头结点为空抛出异常。 + * + * @return the first element from this list + * @throws NoSuchElementException if this list is empty + */ + public E removeFirst() { + final Node f = first; + if (f == null) + throw new NoSuchElementException(); + return unlinkFirst(f); + } + + /** + * Removes and returns the last element from this list. + * + * 从此列表中删除并返回最后一个元素。尾结点为空抛出异常。 + * + * @return the last element from this list + * @throws NoSuchElementException if this list is empty + */ + public E removeLast() { + final Node l = last; + if (l == null) + throw new NoSuchElementException(); + return unlinkLast(l); + } + + /** + * Inserts the specified element at the beginning of this list. + * + * 将指定的元素插入此列表的开头。 + * + * @param e the element to add + */ + public void addFirst(E e) { + linkFirst(e); + } + + /** + * Appends the specified element to the end of this list. + * + *

This method is equivalent to {@link #add}. + * + * 将指定的元素追加到此列表的末尾。此方法等效于add(E) + * + * @param e the element to add + */ + public void addLast(E e) { + linkLast(e); + } + + /** + * Returns {@code true} if this list contains the specified element. + * More formally, returns {@code true} if and only if this list contains + * at least one element {@code e} such that + * (o==null ? e==null : o.equals(e)). + * + * 如果此列表包含指定的元素,则返回true。 + * 更正式地说,当且仅当此列表包含至少一个元素 (o==null ? e==null : o.equals(e)) 时,才返回true。 + * + * @param o element whose presence in this list is to be tested + * @return {@code true} if this list contains the specified element + */ + public boolean contains(Object o) { + return indexOf(o) != -1; + } + + /** + * Returns the number of elements in this list. + * + * 返回此列表中的元素数。 + * + * @return the number of elements in this list + */ + public int size() { + return size; + } + + /** + * Appends the specified element to the end of this list. + * + *

This method is equivalent to {@link #addLast}. + * + * 将指定的元素追加到此列表的末尾。此方法等效于addLast(E) + * + * @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; + } + + /** + * Removes the first occurrence of the specified element from this list, + * if it is present. If this list does not contain the element, it is + * unchanged. More formally, removes the element with the lowest index + * {@code i} such that + * (o==null ? get(i)==null : o.equals(get(i))) + * (if such an element exists). Returns {@code true} if this list + * contained the specified element (or equivalently, if this list + * changed as a result of the call). + * + * 如果存在指定元素,则从该列表中删除第一次出现的该元素。如果此列表不包含该元素,则它保持不变。 + * 更正式地讲,删除索引i最低的元素,使得 (o==null ? get(i)==null : o.equals(get(i)))(如果存在这样的元素)。 + * 如果此列表包含指定的元素(或者等效地,如果此列表由于调用而更改),则返回true。 + * + * @param o element to be removed from this list, if present + * @return {@code true} if this list contained the specified element + */ + public boolean remove(Object o) { + if (o == null) { + //从头结点 first 开始遍历 + for (Node x = first; x != null; x = x.next) { + if (x.item == null) { + unlink(x); + return true; + } + } + } else { + for (Node x = first; x != null; x = x.next) { + if (o.equals(x.item)) { + unlink(x); + return true; + } + } + } + return false; + } + + /** + * Appends all of the elements in the specified collection to the end of + * this list, in the order that they are returned by the specified + * collection's iterator. The behavior of this operation is undefined if + * the specified collection is modified while the operation is in + * progress. (Note that this will occur if the specified collection is + * this list, and it's nonempty.) + * + * 按照指定集合的迭代器返回的顺序,将指定集合中的所有元素追加到此列表的末尾。 + * 如果在操作进行过程中修改了指定的集合,则此操作的行为是不确定的。(请注意,如果指定的集合是此列表,并且是非空的,则将发生这种情况。) + * + * @param c collection containing elements to be added to this list + * @return {@code true} if this list changed as a result of the call + * @throws NullPointerException if the specified collection is null + */ + public boolean addAll(Collection c) { + return addAll(size, c); + } + + /** + * Inserts all of the elements in the specified collection into this + * list, starting at the specified position. Shifts the element + * currently at that position (if any) and any subsequent elements to + * the right (increases their indices). The new elements will appear + * in the list in the order that they are returned by the + * specified collection's iterator. + * + * 从指定位置开始,将指定集合中的所有元素插入此列表。 + * 将当前在该位置的元素(如果有)和任何后续元素右移(增加其索引)。 + * 新元素将按照指定集合的迭代器返回的顺序显示在列表中。 + * + * @param index index at which to insert the first element + * from the specified collection + * @param c collection containing elements to be added to this list + * @return {@code true} if this list changed as a result of the call + * @throws IndexOutOfBoundsException {@inheritDoc} + * @throws NullPointerException if the specified collection is null + */ + public boolean addAll(int index, Collection c) { + //边界检查,如意这里 index 可以等于 size + checkPositionIndex(index); + + //集合转数组,如果数组长度为 0,则返回 false + Object[] a = c.toArray(); + int numNew = a.length; + if (numNew == 0) + return false; + + //succ 是 index 位置的结点,pred 是 succ 的前一个结点。 + //而集合中的所有元素,就是插在 pred 与 succ 之间 + Node pred, succ; + //相等,说明在链表末尾插入 + if (index == size) { + succ = null; + pred = last;//pred 为尾结点 + } else { + succ = node(index); //succ 为 index 位置结点 + pred = succ.prev; //succ 前结点 + } + + //循环插入 + for (Object o : a) { + @SuppressWarnings("unchecked") E e = (E) o; + //创建新结点,其前指针为 pred,如果添加到最后,那最后一个元素肯定是尾结点,所以这里后指针为 null + Node newNode = new Node<>(pred, e, null); + //如果 pred 为空,说明从头部开始插入,那第一个插入的结点就成为头结点 + if (pred == null) + first = newNode; + else + pred.next = newNode; //否则与pred建立双向关联 + pred = newNode; //当前插入的结点就是下一个新插入结点的 pred + } + + //如果 succ 为空,说明从链表尾部开始插入的这些新结点,那最后一个结点就成为了尾结点 + if (succ == null) { + last = pred; + //否则,最后一个插入的结点,与 succ 结点建立双向关联 + } else { + pred.next = succ; + succ.prev = pred; + } + + size += numNew; + //结构性修改计数 + modCount++; + return true; + } + + /** + * Removes all of the elements from this list. + * The list will be empty after this call returns. + * + * 从此列表中删除所有元素。该调用返回后,该列表将为空。 + */ + public void clear() { + // Clearing all of the links between nodes is "unnecessary", but: + // - helps a generational GC if the discarded nodes inhabit + // more than one generation + // - is sure to free memory even if there is a reachable Iterator + //这里循环将每个元素置空不是必须的,然后只要设置 first = last = null 即可。 + //但是对释放缓存与让垃圾回收工作很有帮助 + for (Node x = first; x != null; ) { + Node next = x.next; + x.item = null; + x.next = null; + x.prev = null; + x = next; + } + first = last = null; + size = 0; + //结构性修改计数 + modCount++; + } + + + // Positional Access Operations 位置访问操作 + + /** + * 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; + } + + /** + * Replaces the element at the specified position in this list with the + * specified element. + * + * 用指定元素替换此列表中指定位置的元素。 + * + * @param index index of the element to replace + * @param element element to be stored at the specified position + * @return the element previously at the specified position + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public E set(int index, E element) { + //边界检查 + checkElementIndex(index); + Node x = node(index); + E oldVal = x.item; + x.item = element; + return oldVal; + } + + /** + * 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) { + //边界检查,注意这里 index 可以等于 size + checkPositionIndex(index); + + //说明在链表最后添加 + if (index == size) + linkLast(element); + else + linkBefore(element, node(index)); //在指定位置的结点前添加 + } + + /** + * 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)); + } + + /** + * Tells if the argument is the index of an existing element. + */ + //边界检查 + private boolean isElementIndex(int index) { + return index >= 0 && index < size; + } + + /** + * Tells if the argument is the index of a valid position for an + * iterator or an add operation. + */ + //边界检查,注意这里可以等于 size + private boolean isPositionIndex(int index) { + return index >= 0 && index <= size; + } + + /** + * Constructs an IndexOutOfBoundsException detail message. + * Of the many possible refactorings of the error handling code, + * this "outlining" performs best with both server and client VMs. + */ + private String outOfBoundsMsg(int index) { + return "Index: "+index+", Size: "+size; + } + + //边界检查 + 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 node(int index) { + // assert isElementIndex(index); + + //这里用了双向遍历的方法,判断 index 与 size/2 的关系,然后选择从头遍历还是从尾遍历。 + if (index < (size >> 1)) { + Node x = first; + for (int i = 0; i < index; i++) + x = x.next; + return x; + } else { + Node x = last; + for (int i = size - 1; i > index; i--) + x = x.prev; + return x; + } + } + + // Search Operations 搜索操作 + + /** + * Returns the index of the first occurrence of the specified element + * in this list, or -1 if this list does not contain the element. + * More formally, returns the lowest index {@code i} such that + * (o==null ? get(i)==null : o.equals(get(i))), + * or -1 if there is no such index. + * + * 返回指定元素在此列表中首次出现的索引,如果此列表不包含该元素,则返回-1。 + * 更正式地,返回最低索引 i,使其 (o==null ? get(i)==null : o.equals(get(i))),或者如果没有这样的索引,则返回 -1。 + * + * @param o element to search for + * @return the index of the first occurrence of the specified element in + * this list, or -1 if this list does not contain the element + */ + public int indexOf(Object o) { + int index = 0; + if (o == null) { + for (Node x = first; x != null; x = x.next) { + if (x.item == null) + return index; + index++; + } + } else { + for (Node x = first; x != null; x = x.next) { + if (o.equals(x.item)) + return index; + index++; + } + } + return -1; + } + + /** + * Returns the index of the last occurrence of the specified element + * in this list, or -1 if this list does not contain the element. + * More formally, returns the highest index {@code i} such that + * (o==null ? get(i)==null : o.equals(get(i))), + * or -1 if there is no such index. + * + * 返回指定元素在此列表中最后一次出现的索引,如果此列表不包含该元素,则返回-1。 + * 更正式地,返回最高索引 i,使得 (o==null ? get(i)==null : o.equals(get(i))),或者如果没有这样的索引,则返回 -1。 + * + * @param o element to search for + * @return the index of the last occurrence of the specified element in + * this list, or -1 if this list does not contain the element + */ + public int lastIndexOf(Object o) { + int index = size; + if (o == null) { + for (Node x = last; x != null; x = x.prev) { + index--; + if (x.item == null) + return index; + } + } else { + for (Node x = last; x != null; x = x.prev) { + index--; + if (o.equals(x.item)) + return index; + } + } + return -1; + } + + // Queue operations. 队列操作 + + /** + * Retrieves, but does not remove, the head (first element) of this list. + * + * 检索但不删除此列表的头(第一个元素)。 + * + * @return the head of this list, or {@code null} if this list is empty + * @since 1.5 + */ + public E peek() { + final Node f = first; + return (f == null) ? null : f.item; + } + + /** + * Retrieves, but does not remove, the head (first element) of this list. + * + * 检索但不删除此列表的头(第一个元素)。链表为空抛出异常。 + * + * @return the head of this list + * @throws NoSuchElementException if this list is empty + * @since 1.5 + */ + public E element() { + return getFirst(); + } + + /** + * Retrieves and removes the head (first element) of this list. + * + * 检索并删除此列表的头(第一个元素)。 + * + * @return the head of this list, or {@code null} if this list is empty + * @since 1.5 + */ + public E poll() { + final Node f = first; + return (f == null) ? null : unlinkFirst(f); + } + + /** + * Retrieves and removes the head (first element) of this list. + * + * 检索并删除此列表的头(第一个元素)。链表为空抛出异常。 + * + * @return the head of this list + * @throws NoSuchElementException if this list is empty + * @since 1.5 + */ + public E remove() { + return removeFirst(); + } + + /** + * Adds the specified element as the tail (last element) of this list. + * + * 将指定的元素添加为此列表的尾部(最后一个元素)。 + * + * @param e the element to add + * @return {@code true} (as specified by {@link Queue#offer}) + * @since 1.5 + */ + public boolean offer(E e) { + return add(e); + } + + // Deque operations 双端队列操作 + + /** + * Inserts the specified element at the front of this list. + * + * 将指定的元素插入此列表的开头。 + * + * @param e the element to insert + * @return {@code true} (as specified by {@link Deque#offerFirst}) + * @since 1.6 + */ + public boolean offerFirst(E e) { + addFirst(e); + return true; + } + + /** + * Inserts the specified element at the end of this list. + * + * 将指定的元素插入此列表的末尾。 + * + * @param e the element to insert + * @return {@code true} (as specified by {@link Deque#offerLast}) + * @since 1.6 + */ + public boolean offerLast(E e) { + addLast(e); + return true; + } + + /** + * Retrieves, but does not remove, the first element of this list, + * or returns {@code null} if this list is empty. + * + * 检索但不删除此列表的第一个元素,如果此列表为空,则返回null。 + * + * @return the first element of this list, or {@code null} + * if this list is empty + * @since 1.6 + */ + public E peekFirst() { + final Node f = first; + return (f == null) ? null : f.item; + } + + /** + * Retrieves, but does not remove, the last element of this list, + * or returns {@code null} if this list is empty. + * + * 检索但不删除此列表的最后一个元素,如果此列表为空,则返回null。 + * + * @return the last element of this list, or {@code null} + * if this list is empty + * @since 1.6 + */ + public E peekLast() { + final Node l = last; + return (l == null) ? null : l.item; + } + + /** + * Retrieves and removes the first element of this list, + * or returns {@code null} if this list is empty. + * + * 检索并删除此列表的第一个元素,如果此列表为空,则返回null。 + * + * @return the first element of this list, or {@code null} if + * this list is empty + * @since 1.6 + */ + public E pollFirst() { + final Node f = first; + return (f == null) ? null : unlinkFirst(f); + } + + /** + * Retrieves and removes the last element of this list, + * or returns {@code null} if this list is empty. + * + * 检索并删除此列表的最后一个元素,如果此列表为空,则返回null。 + * + * @return the last element of this list, or {@code null} if + * this list is empty + * @since 1.6 + */ + public E pollLast() { + final Node l = last; + return (l == null) ? null : unlinkLast(l); + } + + /** + * Pushes an element onto the stack represented by this list. In other + * words, inserts the element at the front of this list. + * + *

This method is equivalent to {@link #addFirst}. + * + * 将元素压入此列表表示的堆栈。换句话说,将元素插入此列表的前面。此方法等效于addFirst(E)。 + * + * @param e the element to push + * @since 1.6 + */ + public void push(E e) { + addFirst(e); + } + + /** + * Pops an element from the stack represented by this list. In other + * words, removes and returns the first element of this list. + * + *

This method is equivalent to {@link #removeFirst()}. + * + * 从此列表表示的堆栈中弹出一个元素。换句话说,删除并返回此列表的第一个元素。此方法等效于removeFirst()。 + * + * @return the element at the front of this list (which is the top + * of the stack represented by this list) + * @throws NoSuchElementException if this list is empty + * @since 1.6 + */ + public E pop() { + return removeFirst(); + } + + /** + * Removes the first occurrence of the specified element in this + * list (when traversing the list from head to tail). If the list + * does not contain the element, it is unchanged. + * + * 删除列表中第一次出现的指定元素(从头到尾遍历列表时)。如果列表不包含该元素,则该元素不变。 + * + * @param o element to be removed from this list, if present + * @return {@code true} if the list contained the specified element + * @since 1.6 + */ + public boolean removeFirstOccurrence(Object o) { + return remove(o); + } + + /** + * Removes the last occurrence of the specified element in this + * list (when traversing the list from head to tail). If the list + * does not contain the element, it is unchanged. + * + * 删除该列表中最后一次出现的指定元素(从头到尾遍历列表时)。如果列表不包含该元素,则该元素不变。 + * + * @param o element to be removed from this list, if present + * @return {@code true} if the list contained the specified element + * @since 1.6 + */ + public boolean removeLastOccurrence(Object o) { + if (o == null) { + for (Node x = last; x != null; x = x.prev) { + if (x.item == null) { + unlink(x); + return true; + } + } + } else { + for (Node x = last; x != null; x = x.prev) { + if (o.equals(x.item)) { + unlink(x); + return true; + } + } + } + return false; + } + + /** + * 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)}.

+ * + * The list-iterator is fail-fast: 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. + * + * 从列表中的指定位置开始(按适当顺序)返回此列表中元素的列表迭代器。遵守 List.listIterator(int) 的常规协定。 + * 列表迭代器是快速失败的 : 如果在创建迭代器之后的任何时间对列表进行结构修改,则除了通过列表迭代器自己的 remove 或 add 方法之外,列表迭代器都会抛出 ConcurrentModificationException。 + * 因此,面对并发修改,迭代器将快速而干净地失败,而不是在未来的不确定时间内冒任意,不确定的行为的风险。 + * + * @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 listIterator(int index) { + //边界检查 + checkPositionIndex(index); + //返回迭代器实例 ListItr + return new ListItr(index); + } + + private class ListItr implements ListIterator { + private Node lastReturned; //最后返回的结点 + private Node next; //下一个待返回的结点 + private int nextIndex; //下一个待返回结点的索引 + private int expectedModCount = modCount; //记录结构性修改计数 + + ListItr(int index) { + // assert isPositionIndex(index); + //如果 index == size 说明已经在链表末尾,则 next 为 null,因为已经没有下一个元素了 + next = (index == size) ? null : node(index); + nextIndex = index; + } + + public boolean hasNext() { + return nextIndex < size; + } + + //获取下一个结点的值 + public E next() { + //结构性修改检查 + checkForComodification(); + if (!hasNext()) + throw new NoSuchElementException(); + + lastReturned = next; + next = next.next; + nextIndex++; + return lastReturned.item; + } + + public boolean hasPrevious() { + return nextIndex > 0; + } + + //获取上一个节点的值,获取后 lastReturned = next,也就是说 next -> previous -> next 是相同的结点值 + public E previous() { + //结构性修改检查 + checkForComodification(); + if (!hasPrevious()) + throw new NoSuchElementException(); + + lastReturned = next = (next == null) ? last : next.prev; + nextIndex--; + return lastReturned.item; + } + + public int nextIndex() { + return nextIndex; + } + + public int previousIndex() { + return nextIndex - 1; + } + + //删除最后一次操作的结点。 + //remove 方法在 next 和 previous 方法调用后,只能调用一次。 + public void remove() { + //结构性修改检查 + checkForComodification(); + if (lastReturned == null) + throw new IllegalStateException(); + + Node lastNext = lastReturned.next; + //移除结点 + unlink(lastReturned); + //next 指向到下一个结点,因为 previous 方法会使 next = lastReturned + if (next == lastReturned) + next = lastNext; + else + nextIndex--; //否则只需要 nextIndex 减一即可 + //lastReturned置空 + lastReturned = null; + //结构性修改计数 + expectedModCount++; + } + + //设置值 + public void set(E e) { + if (lastReturned == null) + throw new IllegalStateException(); + //结构性修改检查 + checkForComodification(); + lastReturned.item = e; + } + + //添加结点。在 add 后,不能再调用 remove 或 set方法,因为 add 后,并不会修改 lastReturned 而是置空。 + public void add(E e) { + //结构性修改检查 + checkForComodification(); + //置空 lastReturned + lastReturned = null; + //如果在链表末尾,则直接追加即可 + if (next == null) + linkLast(e); + else + linkBefore(e, next); //否则插入到 next 的结点之前 + nextIndex++; + //结构性修改计数 + expectedModCount++; + } + + //将所有元素应用于 action 函数表达式中。 + public void forEachRemaining(Consumer action) { + Objects.requireNonNull(action); + while (modCount == expectedModCount && nextIndex < size) { + action.accept(next.item); + lastReturned = next; + next = next.next; + nextIndex++; + } + //建构性修改检查 + checkForComodification(); + } + + //建构性修改检查 + final void checkForComodification() { + if (modCount != expectedModCount) + throw new ConcurrentModificationException(); + } + } + + //链表结点定义 + private static class Node { + E item; //值 + Node next; //后指针 + Node prev; //前指针 + + Node(Node prev, E element, Node next) { + this.item = element; + this.next = next; + this.prev = prev; + } + } + + /** + * @since 1.6 + * + * 以相反的顺序返回此双端队列中的元素的迭代器。 元素将按从最后(尾)到第一个(头)的顺序返回。 + */ + public Iterator descendingIterator() { + return new DescendingIterator(); + } + + /** + * Adapter to provide descending iterators via ListItr.previous + */ + private class DescendingIterator implements Iterator { + private final ListItr itr = new ListItr(size()); + public boolean hasNext() { + return itr.hasPrevious(); + } + public E next() { + return itr.previous(); + } + public void remove() { + itr.remove(); + } + } + + @SuppressWarnings("unchecked") + private LinkedList superClone() { + try { + return (LinkedList) super.clone(); + } catch (CloneNotSupportedException e) { + throw new InternalError(e); + } + } + + /** + * Returns a shallow copy of this {@code LinkedList}. (The elements + * themselves are not cloned.) + * + * 返回此LinkedList的浅表副本。(元素本身不会被克隆。) + * + * @return a shallow copy of this {@code LinkedList} instance + */ + public Object clone() { + LinkedList clone = superClone(); + + // Put clone into "virgin" state + clone.first = clone.last = null; + clone.size = 0; + clone.modCount = 0; + + // Initialize clone with our elements + for (Node x = first; x != null; x = x.next) + clone.add(x.item); + + return clone; + } + + /** + * Returns an array containing all of the elements in this list + * in proper sequence (from first to last element). + * + *

The returned array will be "safe" in that no references to it are + * maintained by this list. (In other words, this method must allocate + * a new array). The caller is thus free to modify the returned array. + * + *

This method acts as bridge between array-based and collection-based + * APIs. + * + * @return an array containing all of the elements in this list + * in proper sequence + */ + public Object[] toArray() { + Object[] result = new Object[size]; + int i = 0; + for (Node x = first; x != null; x = x.next) + result[i++] = x.item; + return result; + } + + /** + * Returns an array containing all of the elements in this list in + * proper sequence (from first to last element); the runtime type of + * the returned array is that of the specified array. If the list fits + * in the specified array, it is returned therein. Otherwise, a new + * array is allocated with the runtime type of the specified array and + * the size of this list. + * + *

If the list fits in the specified array with room to spare (i.e., + * the array has more elements than the list), the element in the array + * immediately following the end of the list is set to {@code null}. + * (This is useful in determining the length of the list only if + * the caller knows that the list does not contain any null elements.) + * + *

Like the {@link #toArray()} method, this method acts as bridge between + * array-based and collection-based APIs. Further, this method allows + * precise control over the runtime type of the output array, and may, + * under certain circumstances, be used to save allocation costs. + * + *

Suppose {@code x} is a list known to contain only strings. + * The following code can be used to dump the list into a newly + * allocated array of {@code String}: + * + *

+     *     String[] y = x.toArray(new String[0]);
+ * + * Note that {@code toArray(new Object[0])} is identical in function to + * {@code toArray()}. + * + * @param a the array into which the elements of the list are to + * be stored, if it is big enough; otherwise, a new array of the + * same runtime type is allocated for this purpose. + * @return an array containing the elements of the list + * @throws ArrayStoreException if the runtime type of the specified array + * is not a supertype of the runtime type of every element in + * this list + * @throws NullPointerException if the specified array is null + */ + @SuppressWarnings("unchecked") + public T[] toArray(T[] a) { + if (a.length < size) + a = (T[])java.lang.reflect.Array.newInstance( + a.getClass().getComponentType(), size); + int i = 0; + Object[] result = a; + for (Node x = first; x != null; x = x.next) + result[i++] = x.item; + + //如果参数数组的长度大于 size,则会在 size 位设置一个 null + if (a.length > size) + a[size] = null; + + return a; + } + + private static final long serialVersionUID = 876323262645176354L; + + /** + * 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 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()); + } + + //TODO()...... + /** + * Creates a late-binding + * and fail-fast {@link Spliterator} over the elements in this + * list. + * + *

The {@code Spliterator} reports {@link Spliterator#SIZED} and + * {@link Spliterator#ORDERED}. Overriding implementations should document + * the reporting of additional characteristic values. + * + * @implNote + * The {@code Spliterator} additionally reports {@link Spliterator#SUBSIZED} + * and implements {@code trySplit} to permit limited parallelism.. + * + * @return a {@code Spliterator} over the elements in this list + * @since 1.8 + */ + @Override + public Spliterator spliterator() { + return new LLSpliterator(this, -1, 0); + } + + /** A customized variant of Spliterators.IteratorSpliterator */ + static final class LLSpliterator implements Spliterator { + static final int BATCH_UNIT = 1 << 10; // batch array size increment + static final int MAX_BATCH = 1 << 25; // max batch array size; + final LinkedList list; // null OK unless traversed + Node current; // current node; null until initialized + int est; // size estimate; -1 until first needed + int expectedModCount; // initialized when est set + int batch; // batch size for splits + + LLSpliterator(LinkedList list, int est, int expectedModCount) { + this.list = list; + this.est = est; + this.expectedModCount = expectedModCount; + } + + final int getEst() { + int s; // force initialization + final LinkedList lst; + if ((s = est) < 0) { + if ((lst = list) == null) + s = est = 0; + else { + expectedModCount = lst.modCount; + current = lst.first; + s = est = lst.size; + } + } + return s; + } + + public long estimateSize() { return (long) getEst(); } + + public Spliterator trySplit() { + Node p; + int s = getEst(); + if (s > 1 && (p = current) != null) { + int n = batch + BATCH_UNIT; + if (n > s) + n = s; + if (n > MAX_BATCH) + n = MAX_BATCH; + Object[] a = new Object[n]; + int j = 0; + do { a[j++] = p.item; } while ((p = p.next) != null && j < n); + current = p; + batch = j; + est = s - j; + return Spliterators.spliterator(a, 0, j, Spliterator.ORDERED); + } + return null; + } + + public void forEachRemaining(Consumer action) { + Node p; int n; + if (action == null) throw new NullPointerException(); + if ((n = getEst()) > 0 && (p = current) != null) { + current = null; + est = 0; + do { + E e = p.item; + p = p.next; + action.accept(e); + } while (p != null && --n > 0); + } + if (list.modCount != expectedModCount) + throw new ConcurrentModificationException(); + } + + public boolean tryAdvance(Consumer action) { + Node p; + if (action == null) throw new NullPointerException(); + if (getEst() > 0 && (p = current) != null) { + --est; + E e = p.item; + current = p.next; + action.accept(e); + if (list.modCount != expectedModCount) + throw new ConcurrentModificationException(); + return true; + } + return false; + } + + public int characteristics() { + return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED; + } + } + +} +``` -- Gitee From fe006a427b999ddb8a742035314eea6441a683c6 Mon Sep 17 00:00:00 2001 From: RDCynthia Date: Sun, 8 Mar 2020 12:28:28 +0800 Subject: [PATCH 4/4] Commit HashMap Homework --- second/week_01/81/HashMap.md | 269 +++++++++++++++++++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 second/week_01/81/HashMap.md diff --git a/second/week_01/81/HashMap.md b/second/week_01/81/HashMap.md new file mode 100644 index 0000000..70d2ed4 --- /dev/null +++ b/second/week_01/81/HashMap.md @@ -0,0 +1,269 @@ +### 接口结构 + +``` +HashMap extends AbstractMap implements Map, Cloneable, Serializable +``` +HashMap 与 Hashtable 大致上相同,除了 HashMap 是非同步安全的,并且允许null key 与 null value。 + +HashMap不保证map 中元素的顺序,尤其是不保证该顺序恒久不变。 + +假设哈希函数将元素正确的分散在存储桶中,则 get 与 put 基本操作的性能是恒定时间的。 + +Iteration迭代元素的时间,与HashMap 实例的"容量"(存储桶的数量)及其大小(key-value映射数)成正比,因此,如果迭代性能很重要,则不要将初始容量设置得过高(或负载因子过低),这一点非常重要。 + +HashMap 的桶容量要求必须是 2 的 N 次幂(在较老的版本是质数),因为对现代的处理器来说,除法与求余数是最慢的操作。使用 2 的整数次方长度的散列表,可用掩码代替除法。因为 get() 是使用最多的操作,求余数的%操作是其开销最大的部分,而使用 2 的整数次方可以消除此开销。 +``` +调整初始容量为 2^N +static final int tableSizeFor(int cap) { + int n = cap - 1; + n |= n >>> 1; + n |= n >>> 2; + n |= n >>> 4; + n |= n >>> 8; + n |= n >>> 16; + return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; +} +``` +HashMap的增容,根据 `threshold` 阈值而定。当桶内的数据量超过阈值时,便开始扩容。 +阈值的计算为 *桶容量* * *加载因子*,比如桶容量 16,加载因子0.75。则阈值为:16 * 0.75 + +加载因子默认为 0.75,它是经过大量实践得出来的一个经验值。当加载因子设置的比较小的时候,阀值就会相应的变小,扩容次数就会变多,这就会导致 HashMap 的容量使用不充分,会频繁扩容,浪费内存,扩容效率还很低;当加载因子设置的又比较大的时候呢,阀值变大了,扩容次数少了,容量使用率又提高了,但会导致 hash 冲突的概率增加。 + +### 常用实现对比 + +> 如果没有其他限制,应该默认使用 HashMap,因为它对速度进行了优化。其他实现强调了其他特性,因此都不如 HashMap 快 + +- HashMap Map 基于散列表的实现(它取代了 Hashtable)。插入和查询“键值对”的开销是固定的。它可以通过构造器设置`容量`和`负载因子`,以调整容器的性能。 + +- LinkedHashMap 类似于HashMap,但是迭代遍历它时,取得“键值对”的顺序是其插入次序,或者是最近最少使用(LRU)的次序。只比 HashMap 慢一点,而在迭代访问时反而更快,因为它使用链表维护内部次序 + +- TreeMap 基于红黑树的实现。查看“键”或“键值对”时,他们会被排序(次序由 Comparable 或 Comparator 决定)。TreeMap 的特点在于,所得到的的结果是经过排序的。TreeMap 是唯一的带有 subMap()方法的 map,可以返回一个子集。 + +- WeakHashMap 弱键映射,允许释放映射所指向的对象,是为某类特殊问题而设计的。如果映射之外没有引用指向某个“键”,则此“键”可以被垃圾收集器回收。 + +- ConcurrentHashMap 一种线程安全的 map,它不涉及同步枷锁 + +- IdentityHashMap 使用==带起 equals()对“键”进行比较的散列映射,为某类特殊问题设计。 + +任何键都必须具有一个 equals() 方法,如果键被用于散列 Map,那么他必须还具有恰当的 hashCode()方法,如果键被用于 TreeMap,那么它必须实现 Comparable。 + +### 散列与散列码 + +如果使用标准类库的类作为 HashMap 的键,则表现的很好,因为其具备了键所需要的全部性质。如果使用自己的类去作为键的话,则需要重写 hashcode()与 equals()方法。 + +- hashcode() + +hashcode 用来生成与查询桶的下标,如果类未重写 hashcode,则默认使用 Object 继承而来的 hashcode,而默认的 hashcode 使用对象地址的hash 值。这样就会造成,每次传递给 HashMap 的键hash 值不同,而无法确定是否是相同键。比如以下代码 + +``` +public class Tests { + static class MockKey { + int num; + + MockKey(int num) { + this.num = num; + } + + /*@Override + public boolean equals(Object o) { + //instanceof 会检查左边的对象是否为 null,是的话会返回 false + return o instanceof MockKey && num == ((MockKey) o).num; + }*/ + + /*@Override + public int hashCode() { + return num; + }*/ + } + + public static void main(String[] args) { + Map map = new HashMap<>(); + + for (int i = 0; i < 10; i++) + map.put(new MockKey(i), i); + System.out.println(map); + + MockKey mk = new MockKey(3); + if (map.containsKey(mk)) + System.out.println("found it : " + map.get(mk)); + else + System.out.println("not found"); + } +} +``` +定义一个MockKey内部类,并用此内部类作为 HashMap 的键。在主函数中,先对 Map 初始化,然后查询键为`MockKey(3)`的值。此时打印出的结果是`not found`的。原因是,HashMap 默认使用对象的内存地址的 hash 值在桶中查找,而由于不是同一个MockKey对象,结果则是找不到。 + +这个时候,则需要重写 hashcode,例如上面例子被注释掉的 hashCode()(如何重写hashCode,请往下看)。重写 hashCode 方法后,代码依然不能正确运行,因为还需要重写 equals()方法。 + +- equals() + +equals 用来判断当前的键是否与表中存在的键相同,如果不重写也是默认继承自 Obejct。例如上面的例子,由于MockKey实例不同,所以默认的 equals 返回 false,所以在相同的hash 下标中,遍历冲突链表找不到相同的键。 + +正确的 equals()方法必须满足下列 5 个条件: +1. 自反性。对任意 x,x.equals(x)一定返回 true。 +2. 对称性。对任意 x 和 y,如果 y.equals(x)返回 true,则 x.equals(y)也返回 true。 +3. 传递性。对任意 x,y,z,如果有 x.equals(y)返回 true,y.equals(z)返回 ture,则 x.equals(z)一定返回 true +4. 一致性。对任意 x 和 y,如果对象中用于等价比较的信息没有改变,那么无论调用 x.equals(y)多少次,返回的结果应该保持一致,要么一直是 true,要么一直是 false。 +5. 对任何不是 null 的 x,x.equals(null)一定返回 false。 + +例如上例注释的 equals()方法,重写后,即可返回正确的结果。 + +- 理解散列 + +散列用数组来表示键的信息(这里是键的信息,而不是键本身)。但是数组不能调整容量,如何保存数量不确定的值呢?答案是,数组并不保存键本身,而是通过键对象生成一个数字,将其作为数组的下标,不同的键可以产生相同的下标,这个数字就是散列码。而生成散列码的函数 hashCode()也被称为散列函数。这个数组也被称为桶。 + +如果下标产生冲突怎么办呢?所以数组中,并不直接保存值,而是保存值的 list。对 list 中,使用 equals()方法进行线性查找。 + +这样在HashMap 中找值就是变成,先找键对应的 hash 值,直接跳到数组对应的位置,然后这个位置的值 list 中,线性比较key,找到返回值即可。 + +- 如何设计自己的散列函数 + +设计 hashCode最重要的因素就是,无论何时,对同一个对象调用 hashCode都应该生成同样的值。散列码不必是唯一的,但是速度要快,而且有意义,则必须基于对象的内容生成散列码。并且,通过 hashCode()和equals()必须能完全确定对象的身份。 + +设计散列函数,可以遵循以下指导 +1. 给 int 变量 result 赋予某个非零值常量,比如 17. +2. 为对象内每个有意义的域f(即每个可以做 equals()操作的域)计算出一个 int 散列码 c: + +``` +boolean c = (f ? 0 : 1) +byte、char、short、int c = (int) f +long c = (int)(f ^ (f >>> 32)) +float c = Float.floatToIntBits(f) +double long l = Double.doubleToLongBits(f);c = (int)(l ^ (l >>> 32)) +Object,其 equals()调用这个域的 equals() c = f.hashCode() +数组 对每个元素应用上述规则 +``` + +3. 合并计算得到的散列码 + +``` +result = 37 * result + c; +``` + +4. 返回 result +5. 检查 hashCode() 最后生成的结果,确保相同的对象有相同的散列码。 + +举例: +``` +class test{ + ...... + private String str; + private int num; + ...... + + //当 str 相同时,如果 id 不同,生成的散列值也不同 + public int hashCode(){ + int result = 17; + result = 37 * result + s.hashCode(); + result = 37 * result + id; + return result; + } +} +``` + +### 源码分析 + +V get(Object key) + +``` +public V get(Object key) { + Node e; + //调用getNode方法 + return (e = getNode(hash(key), key)) == null ? null : e.value; + } + +//(n-1) & m 这个操作其实就是 m % n +final Node getNode(int hash, Object key) { + Node[] tab; Node first, e; int n; K k; + //如果表不为空,长度大于零,并且 hash 值对应下标的第一个节点不为空,则进行取值操作 + if ((tab = table) != null && (n = tab.length) > 0 && + (first = tab[(n - 1) & hash]) != null) { + //如果第一个节点的 key 与参数 key 相同,则返回第一个节点 + if (first.hash == hash && // always check first node + ((k = first.key) == key || (key != null && key.equals(k)))) + return first; + //否则,从第二个节点开始遍历 + if ((e = first.next) != null) { + //转为红黑树 + if (first instanceof TreeNode) + return ((TreeNode)first).getTreeNode(hash, key); + do { + //如果找到则返回 + if (e.hash == hash && + ((k = e.key) == key || (key != null && key.equals(k)))) + return e; + } while ((e = e.next) != null); + } + } + return null; + } +``` + +V put(K key, V value) + +``` +public V put(K key, V value) { + return putVal(hash(key), key, value, false, true); + } + +final V putVal(int hash, K key, V value, boolean onlyIfAbsent, + boolean evict) { + Node[] tab; Node p; int n, i; + //如果表为空,则进行容量初始化 + if ((tab = table) == null || (n = tab.length) == 0) + n = (tab = resize()).length; + //如果 hash 值对应的下标出节点为空,则直接插入新节点即可 + if ((p = tab[i = (n - 1) & hash]) == null) + tab[i] = newNode(hash, key, value, null); + else { + Node e; K k; + //如果头结点存在,则判断是否和给定参数 key 相同 + if (p.hash == hash && + ((k = p.key) == key || (key != null && key.equals(k)))) + e = p; + //判断当前结构是红黑树还是链表,做不同的搜索处理 + else if (p instanceof TreeNode) + e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value); + else { + //从第二个节点遍历 + for (int binCount = 0; ; ++binCount) { + //如果下一个节点为空 + if ((e = p.next) == null) { + //添加一个新节点到头节点下 + p.next = newNode(hash, key, value, null); + //当hash冲突到一定程度时,把链表转为红黑树结构 + if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st + treeifyBin(tab, hash); + break; + } + //找到则跳出循环 + if (e.hash == hash && + ((k = e.key) == key || (key != null && key.equals(k)))) + break; + p = e; + } + } + //如果找到值 + if (e != null) { // existing mapping for key + //返回旧值 + V oldValue = e.value; + //用新值代替旧值 + if (!onlyIfAbsent || oldValue == null) + e.value = value; + //貌似啥也没干 + afterNodeAccess(e); + return oldValue; + } + } + //记录修改次数+1 + ++modCount; + //如果当前数据容量大于阈值,则扩容 + if (++size > threshold) + resize(); + //貌似啥也没干 + afterNodeInsertion(evict); + return null; + } +``` + +TODO() 源码分析还在继续中,之后会补全的。 \ No newline at end of file -- Gitee