List接口以及其实现类的源码分析
List接口官方解释:
有序集合(也称为序列)。 此界面的用户可以精确控制每个元素在列表中的插入位置。 用户可以通过它们的整数索引(在列表中的位置)访问元素,并在列表中搜索元素。
与集合不同,列表通常允许重复元素。 更正式地,列表通常允许元素对e1和e2使得e1.equals(e2) ,并且如果它们完全允许 null 元素,它们通常允许多个 null 元素。 有人可能希望通过在用户尝试插入它们时抛出运行时异常来实现一个禁止重复的列表,这并非不可想象,但我们希望这种用法很少见。
List接口在迭代器、 add 、 remove 、 equals和hashCode方法的契约上放置了除Collection接口中指定的那些之外的其他规定。 为方便起见,此处还包括其他继承方法的声明。
List接口提供了四种对列表元素进行位置(索引)访问的方法。 列表(如 Java 数组)是从零开始的。 请注意,对于某些实现(例如LinkedList类),这些操作的执行时间可能与索引值成正比。 因此,如果调用者不知道实现,则迭代列表中的元素通常比通过它进行索引更可取。
List接口提供了一个特殊的迭代器,称为ListIterator ,除了Iterator接口提供的正常操作之外,它还允许元素插入和替换以及双向访问。 提供了一种方法来获取从列表中的指定位置开始的列表迭代器。
List接口提供了两种方法来搜索指定的对象。 从性能的角度来看,应谨慎使用这些方法。 在许多实现中,它们将执行代价高昂的线性搜索。
List接口提供了两种方法来有效地在列表中的任意点插入和删除多个元素。
注意:虽然允许列表将自身包含为元素,但建议格外小心: equals和hashCode方法不再在此类列表上明确定义。
一些列表实现对它们可能包含的元素有限制。 例如,有些实现禁止空元素,有些实现对其元素的类型有限制。 尝试添加不合格的元素会引发未经检查的异常,通常为NullPointerException或ClassCastException 。 尝试查询不合格元素的存在可能会引发异常,或者可能只是返回 false; 一些实现会表现出前一种行为,而另一些会表现出后者。 更一般地,尝试对不合格元素执行操作,其完成不会导致将不合格元素插入列表中,可能会引发异常,或者可能会成功,具体取决于实现的选择。 在此接口的规范中,此类异常被标记为“可选”。
此接口是Java Collections Framework的成员。
List的两个常用的实现类ArrayList和LinkedList源码分析
ArrayList:
官方文档解释:
List接口的可调整大小的数组实现。 实现所有可选的列表操作,并允许所有元素,包括null 。 除了实现List接口之外,该类还提供了操作内部用于存储列表的数组大小的方法。 (这个类大致相当于Vector ,只是它是不同步的。)
size 、 isEmpty 、 get 、 set 、 iterator和listIterator操作在恒定时间内运行。 add操作在分摊常数 time 内运行,即添加 n 个元素需要 O(n) 时间。 所有其他操作都在线性时间内运行(粗略地说)。 与LinkedList实现相比,常量因子较低。
每个ArrayList实例都有一个capacity 。 容量是用于存储列表中元素的数组的大小。 它始终至少与列表大小一样大。 随着元素被添加到 ArrayList,它的容量会自动增长。 除了添加元素具有恒定的摊销时间成本之外,没有指定增长政策的细节。
应用程序可以在使用ensureCapacity操作添加大量元素之前增加ArrayList实例的容量。 这可以减少增量重新分配的量。
请注意,此实现不是同步的。 如果多个线程同时访问一个ArrayList实例,并且至少有一个线程在结构上修改了列表,则必须在外部进行同步。 (结构修改是添加或删除一个或多个元素,或显式调整后备数组大小的任何操作;仅设置元素的值不是结构修改。)这通常是通过同步一些自然封装的对象来实现的列表。 如果不存在此类对象,则应使用Collections.synchronizedList方法“包装”该列表。 这最好在创建时完成,以防止对列表的意外不同步访问:
List list = Collections.synchronizedList(new ArrayList(...));
此类的iterator和listIterator方法返回的iterator是快速失败的:如果在创建迭代器后的任何时间以任何方式修改列表结构,除了通过迭代器自己的remove或add方法,迭代器将抛出ConcurrentModificationException 。 因此,面对并发修改,迭代器快速而干净地失败,而不是冒着在未来不确定的时间出现任意、非确定性行为的风险。
请注意,无法保证迭代器的快速失败行为,因为一般而言,在存在非同步并发修改的情况下不可能做出任何硬保证。 快速失败的迭代器会尽最大努力抛出ConcurrentModificationException 。 因此,编写一个依赖此异常来确保其正确性的程序是错误的:迭代器的快速失败行为应该仅用于检测错误。
源码分析:
private static final int DEFAULT_CAPACITY = 10;
初始容量为10
private static final Object[] EMPTY_ELEMENTDATA = {};
用于空实例的共享空数组实例。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
用于默认大小的空实例的共享空数组实例。 我们将其与 EMPTY_ELEMENTDATA 区分开来,以了解添加第一个元素时要膨胀多少。
transient Object[] elementData;
ArrayList 的元素存储在其中的数组缓冲区。 ArrayList 的容量就是这个数组缓冲区的长度。 添加第一个元素时,任何带有 elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的空 ArrayList 都将扩展为 DEFAULT_CAPACITY。
public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } }
构造一个具有指定初始容量的空列表
public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
构造一个初始容量为 10 的空列表
public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); //该方法返回的数组是安全的,因为此集合不维护该数组的引用,因此需要创建一个新的数组,可以用于修改。 if ((size = elementData.length) != 0) { // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class);//因为有些不会返回object类型的数组 } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } }
按照集合的迭代器返回的顺序构造一个包含指定集合元素的列表。
public void trimToSize() { modCount++; if (size < elementData.length) { elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size); } }
将此ArrayList实例的容量修剪为列表的当前大小。 应用程序可以使用此操作来最小化ArrayList实例的存储空间。其中modCount是此列表在结构上被修改的次数。 结构修改是那些改变列表大小的修改,或者以其他方式扰乱它,以致正在进行的迭代可能会产生不正确的结果。此字段由返回的迭代器和列表迭代器实现中使用iterator和listIterator方法。 如果此字段的值意外更改,迭代器(或列表迭代器)将抛出ConcurrentModificationException以响应next 、 remove 、 previous 、 set或add操作。 这提供了快速失败行为,而不是在迭代期间面对并发修改时的非确定性行为。子类使用此字段是可选的。 如果一个子类希望提供快速失败的迭代器(和列表迭代器),那么它只需要在它的add(int, E)和remove(int)方法(以及它覆盖的导致结构化的任何其他方法中增加这个字段add(int, E)列表的修改)。 对add(int, E)或remove(int)的单个调用必须向该字段添加不超过一个,否则迭代器(和列表迭代器)将抛出虚假的ConcurrentModificationExceptions 。 如果实现不希望提供快速失败的迭代器,则可以忽略此字段。
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
要分配的数组的最大大小。 一些 VM 在数组中保留一些头字。 尝试分配更大的数组可能会导致 OutOfMemoryError:请求的数组大小超出 VM 限制
public void ensureCapacity(int minCapacity) { int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) // any size if not default element table ? 0 // larger than default for default empty table. It's already // supposed to be at default size. : DEFAULT_CAPACITY; if (minCapacity > minExpand) { ensureExplicitCapacity(minCapacity); } }//判断是否为默认空表,如果为默认空表就将容量设置为10
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}//判断需要扩容的大小是否大于列表的容量
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);//设置新的容量为原来的1.5倍
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;//如果将要扩容的容量小于最小的容量就修改容量
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);//如果大于最大限制,就设置为Integer的最大值
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);//创建一个新数组将数据复制到这个新数组
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
如有必要,增加此ArrayList实例的容量,以确保它至少可以容纳由最小容量参数指定的元素数。
public int size() { return size; }
返回此列表中的元素数。
public boolean isEmpty() { return size == 0; }
如果不包含任何元素,则返回true
public boolean contains(Object o) { return indexOf(o) >= 0; }
public int indexOf(Object o) {
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;
}
return -1;
} //返回此列表中指定元素第一次出现的索引,如果此列表不包含该元素,则返回 -1。 更正式地,返回最低索引i使得(o==null ? get(i)==null : o.equals(get(i))) ,如果没有这样的索引,则返回 -1 。
如果此列表包含指定的元素,则返回true 。 更正式地说,当且仅当此列表包含至少一个元素e使得(o==null ? e==null : o.equals(e))时才返回true 。(主要判断是否为null以及是否相等)
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; }//和indexof类似,不过是从后往前遍历数组
返回此列表中指定元素最后一次出现的索引,如果此列表不包含该元素,则返回 -1。 更正式地,返回最高索引i使得(o==null ? get(i)==null : o.equals(get(i))) ,如果没有这样的索引,则返回 -1 。
public Object clone() { try { 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); } }
返回此ArrayList实例的浅表副本。 (元素本身不会被复制)super.clone()是调用object类的方法
public Object[] toArray() { return Arrays.copyOf(elementData, size); }
以适当的顺序(从第一个元素到最后一个元素)返回一个包含此列表中所有元素的数组。
返回的数组将是“安全的”,因为此列表不维护对它的引用。 (换句话说,此方法必须分配一个新数组)。 因此调用者可以自由地修改返回的数组。
此方法充当基于数组和基于集合的 API 之间的桥梁。
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
以适当的顺序(从第一个元素到最后一个元素)返回一个包含此列表中所有元素的数组; 返回数组的运行时类型是指定数组的类型。 如果列表适合指定的数组,则在其中返回。 否则,将使用指定数组的运行时类型和此列表的大小分配一个新数组。
如果列表适合指定的数组并有剩余空间(即,数组的元素比列表多),则数组中紧跟在集合末尾之后的元素将设置为null 。 (仅当调用者知道列表不包含任何空元素时,这才有助于确定列表的长度。)
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
位置访问操作
public E get(int index) { rangeCheck(index); return elementData(index); }
返回此列表中指定位置的元素
private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }
检查给定的索引是否在范围内。 如果不是,则抛出适当的运行时异常。 此方法*不*检查索引是否为负数:它总是在数组访问之前立即使用,如果索引为负数,则抛出 ArrayIndexOutOfBoundsException。
public E set(int index, E element) { rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue;//返回之前在指定位置的元素 }
用指定的元素替换此列表中指定位置的元素。
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
将指定的元素附加到此列表的末尾。
public void add(int index, E element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index);//将将列表往后复制一个,空出一个位置来给新添加的元素 elementData[index] = element; size++; }
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
} //判断索引是否超过列表元素的大小
在此列表中的指定位置插入指定元素。 将当前在该位置的元素(如果有)和任何后续元素向右移动(向它们的索引添加一个)。
public E remove(int index) { rangeCheck(index); modCount++; E oldValue = elementData(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 return oldValue; }
移除此列表中指定位置的元素。 将任何后续元素向左移动(从它们的索引中减去一个)。
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; }
从此列表中删除第一次出现的指定元素(如果存在)。 如果列表不包含该元素,则它保持不变。 更正式地,删除具有最低索引i的元素,使得(o==null ? get(i)==null : o.equals(get(i))) (如果这样的元素存在)。 如果此列表包含指定的元素(或等效地,如果此列表因调用而更改),则返回true 。
private void fastRemove(int index) { modCount++; int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work }
跳过边界检查并且不返回移除的值的私有移除方法。
public void clear() { modCount++; // clear to let GC do its work for (int i = 0; i < size; i++) elementData[i] = null; size = 0; }
从此列表中删除所有元素。 此调用返回后,列表将为空。
public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); // Increments modCount System.arraycopy(a, 0, elementData, size, numNew); size += numNew; return numNew != 0; }
按照指定集合的迭代器返回的顺序,将指定集合中的所有元素追加到此列表的末尾。 如果在操作进行时修改了指定的集合,则此操作的行为未定义。 (这意味着如果指定的集合是这个列表,并且这个列表是非空的,那么这个调用的行为是未定义的。)
public boolean addAll(int index, Collection<? extends E> c) { rangeCheckForAdd(index); Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); // Increments modCount int numMoved = size - index; if (numMoved > 0) System.arraycopy(elementData, index, elementData, index + numNew, numMoved); System.arraycopy(a, 0, elementData, index, numNew); size += numNew; return numNew != 0; }
从指定位置开始,将指定集合中的所有元素插入此列表。 将当前在该位置的元素(如果有)和任何后续元素向右移动(增加它们的索引)。 新元素将按照指定集合的迭代器返回的顺序出现在列表中。
protected void removeRange(int fromIndex, int toIndex) { modCount++; int numMoved = size - toIndex; System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved); // clear to let GC do its work int newSize = size - (toIndex-fromIndex); for (int i = newSize; i < size; i++) { elementData[i] = null; } size = newSize; }
从此列表中删除其索引介于fromIndex和toIndex之间(不包括在内)的所有元素。 将任何后续元素向左移动(减少它们的索引)。 此调用通过(toIndex - fromIndex)元素缩短列表。 (如果toIndex==fromIndex ,则此操作无效。)
public boolean removeAll(Collection<?> c) { Objects.requireNonNull(c); return batchRemove(c, false); }
从此列表中删除包含在指定集合中的所有元素
private boolean batchRemove(Collection<?> c, boolean complement) { final Object[] elementData = this.elementData; int r = 0, w = 0; boolean modified = false; try { for (; r < size; r++) if (c.contains(elementData[r]) == complement) elementData[w++] = elementData[r]; } finally { // Preserve behavioral compatibility with AbstractCollection, // even if c.contains() throws. if (r != size) { System.arraycopy(elementData, r, elementData, w, size - r); w += size - r; } 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; }
LinkedList见下一个文章。

浙公网安备 33010602011771号