Java集合 List实现类 ArrayList 实现浅析
文章目录
- Java集合 List实现类 ArrayList 实现浅析
- 一、List 简述(来自ArrayList注释)
- 二、构造方法
- 三、List 方法
- 1. 添加元素 add(E e)
- 2. 获取元素 get(index)
- 3.删除指定位置的一个元素 remove(int index)
- 4.删除指定元素,首次出现的元素 remove(object)
- 5.修改元素 set(index, element)
- 6.添加元素到指定位置 add(index, element)
- 7.获取列表元素的数量 size()
- 8.判断列表是否为空 isEmpty()
- 9.搜索元素 indexOf(object)
- 10.搜索最后出现此元素的索引 lastIndexOf(object)
- 11.判断是否包含指定元素 contains(object)
- 12. 清空列表 clear()
- 13.list 转数组 toArray()
- 14.list 转数组 toArray(T[] a) (返回运行时类型)
- 15 . 排序 sort(Comparator<? super E> c)
- 16. 迭代 forEach(Consumer<? super E> action)
- ArrayList 的克隆与序列化
- 四、迭代器 Iterator
- 1) 获取迭代器对象iterator()
- 2) 获取列表迭代器 listIterator()
- 3) 获取从指定位置开始的列表迭代器 listIterator(index)
- 4) Itr 实现 Iterator 源码分析
- 5) ListItr 源码实现
- 扩展阅读
一、List 简述(来自ArrayList注释)
List 接口的大小可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。(此类大致上等同于 Vector 类,除了此类是不同步的。)
size、isEmpty、get、set、iterator 和 listIterator 操作都以固定时间运行。add 操作以分摊的固定时间 运行,也就是说,添加 n 个元素需要 O(n) 时间。其他所有操作都以线性时间运行(大体上讲)。与用于 LinkedList 实现的常数因子相比,此实现的常数因子较低。
每个 ArrayList 实例都有一个容量。该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向 ArrayList 中不断添加元素,其容量也自动增长。并未指定增长策略的细节,因为这不只是添加元素会带来分摊固定时间开销那样简单。
在添加大量元素前,应用程序可以使用 ensureCapacity 操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。
注意,此实现不是同步的。 如果多个线程同时访问一个 ArrayList 实例,而其中至少一个线程从结构上修改了列表,那么它必须 保持外部同步。(结构上的修改是指任何添加或删除一个或多个元素的操作,或者显式调整底层数组的大小;仅仅设置元素的值不是结构上的修改。)这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedList 方法将该列表“包装”起来。这最好在创建时完成,以防止意外对列表进行不同步的访问:
List list = Collections.synchronizedList(new ArrayList(...));
此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:在创建迭代器之后,除非通过迭代器自身的 remove 或 add 方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。
注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法:迭代器的快速失败行为应该仅用于检测 bug。
类图
ArrayList 源码实现基于 jdk 1.8
源代码来源于 jdk 1.8
二、构造方法
ArrayList 字段说明
// 初始默认容量为10
private static final int DEFAULT_CAPACITY = 10;
// 存放元素的数组
transient Object[] elementData;
// 这个值是实际数组包含元素的数量, 注意这个值不是数组的大小
private int size;
// 长度为零的非空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
1) 无参的构造方法
源码注释: 构造一个初始容量为十的空列表。
源码分析: DEFAULTCAPACITY_EMPTY_ELEMENTDATA是个 {}, 即空数组, 为什么初始容量为10? 下面看 add 方法时再解析*
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
2) 参数为int 的构造方法
源码注释: 构造具有指定初始容量的空列表。
源码分析: 如果参数 initcap > 0,则创建指定容量的里边,
如果 == 0,则创建空的数组,参数为0的情况效果和无参构造方法一样,因为 EMPTY_ELEMENTDATA = {}, DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {},
如果 < 0; 则抛出异常 IllegalArgumentException(合法或不正确的参数);
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);
}
}
3) 创建指定集合的列表
源码注释: 构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序。
源码分析: 参数 c 调用toArray()方法转换为数组赋给列表的属性elementData ,
如果集合 c 元素为0, 则创建默认容量为空的列表;
如果c > 0, 则判断类型, 再复制数组,此处具有填充的效果*
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
this.elementData = EMPTY_ELEMENTDATA;
}
}
三、List 方法
1. 添加元素 add(E e)
源码注释: (添加一个元素到列表末尾)
源码分析:
添加元素前准备.:
a:数组初始化 ensureCapacityInternal()
b: 计数器 +1 ensureExplicitCapacity()
c: 数组是否已满,是否需要扩容 grow()
d: 把元素添加到数组里: elementData[size++] = e;
// 这是在AbstractList里的一个瞬时变量用于控制list并发操作的计数器
protected transient int modCount = 0;
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
/*
* a. 添加元素前的准备, 调用方法ensureCapacityInternal(size + 1) ,
* 判断 elementData是否为 {}空的未初始化的列表,如果 == {}, 则 返回默认的大小
* 如果 elementData != {}, 则调用方法ensureExplicitCapacity(minCapacity)
*/
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
/*
* b.
* 判断当前列表元素数量 + 1是否超过了elementData.length 的大小,
* 计算器 modCount++ 加1;
* 如果 >,则调用 grow(minCapacity)方法进行扩容;
*/
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/*
* c. 扩容:
* 数组elementData 的容量扩大为原来的 1.5倍, 然后复制元素到新数组,
* 有源码可知, ArrayList运许创建的列表容量最大值为, Integer.MAX_VALUE = (2^32 - 1)
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
2. 获取元素 get(index)
源码注释: 返回此列表中指定位置的元素。
源码分析: 传入数组的索引获取元素前要检查索引是否越界,
如果越界,则抛出异常 IndexOutOfBoundsException,最后调用 elementData(int index) 方法直接返回元素 E;
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
// 检查索引是否越界
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
3.删除指定位置的一个元素 remove(int index)
源码注释: 删除该列表中指定位置的元素。
源码分析: 删除指定位置的元素,先检查下标再获取此元素; 最后调用 System 类中的arraycopy()方法把除去index位置的元素的其他元素全部左移,最后一个元素 elementData[–size] = null;最终数组的大小不变只是末尾的一个元素置为null
最终结果就像这样
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
[0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 14]
[0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, null]
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;
}
关于 System.arraycopy() 方法实现这里不做探讨
public static native void arraycopy(Object src, int srcPos, dest, int destPos, length);
属性 | 说明 |
---|---|
src | 源数组 |
srcPos | 源数组中的起始位置 |
dest | 目标数组 |
destPos | 目标数据中的起始位置 |
length | 要复制的数组元素的数量 |
4.删除指定元素,首次出现的元素 remove(object)
源码注释: 从列表中删除指定元素的第一个出现(如果存在)。
源码解析: 移除首次出现的指定元素, 循环size长度的数组, 如果 o == null , 则判断(elementData[index] == null), 如果 o != null , 则 o.equals(elementData[index]),
最后调用 fastremove(index) 从新拷贝元素;*
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 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
}
5.修改元素 set(index, element)
源码注释: 用指定的元素替换此列表中指定位置的元素
源码解析: 修改指定位置的元素, 首先检查index 是否越界, 然后把index 位置的元素替换位当前元素
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
- 以上5个操作是基本的crud操作, 下面要探讨List的其他操作
6.添加元素到指定位置 add(index, element)
源码注释: 在此列表中的指定位置插入指定的元素。
源码解析: 添加元素到指定位置时, 先检查下标是否越界, 再判断当前数组是否已满,
最后复制数组,把指定元素替换到数组里
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++;
}
7.获取列表元素的数量 size()
源码分析: 直接返回列表的 size 属性;
public int size() {
return size;
}
8.判断列表是否为空 isEmpty()
源码分析: 如果列表的长度为 0, 则列表为空表; 这里所指的空是列表元素为0, 而不是 list 对象为 null
public boolean isEmpty() {
return size == 0;
}
9.搜索元素 indexOf(object)
源码注释: 返回此列表中指定元素的第一次出现的索引,如果此列表不包含元素,则返回-1。
源码分析: 这里的算法和 remove(object) 方法一样,从索引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;
}
10.搜索最后出现此元素的索引 lastIndexOf(object)
源码注释: 返回此列表中指定元素的最后一次出现的索引,如果此列表不包含元素,则返回-1。
源码解析: 此处是从后往前遍历的循环, 逻辑是 indexOf() 方法的对立;
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;
}
11.判断是否包含指定元素 contains(object)
源码注释: (如果列表包含此元素, 则返回 true 否则 false)
源码分析: 从源码看, 此方法是 indexOf()的扩展.
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
12. 清空列表 clear()
源码注释: (从此列表中删除所有元素。 此调用返回后,列表将为空)
源码分析: for循环把数组所有引用置空, size = 0
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
13.list 转数组 toArray()
源码注释: 以正确的顺序(从第一个到最后一个元素)返回一个包含此列表中所有元素的数组。
源码分析: 此方法返回一个 Object[] 类型的数组, 无法返回目标泛型的数组, 无法强制转换为目标数组, 比如 (String[])list.toArray() 会抛出异常 ClassCastException, 一般不推荐使用
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
14.list 转数组 toArray(T[] a) (返回运行时类型)
源码注释: 以正确的顺序返回一个包含此列表中所有元素的数组(从第一个到最后一个元素); 返回的数组的运行时类型是指定数组的运行时类型。
源码分析: 从源码看, 如果 传入的数组a 小于列表的元素的数量,则调用 Arrays.copyOf()方法;否则 调用 System.arraycopy() 方法, 最后多出的部分,分配null;
推荐使用此转换方法:
String[] ss = {};
System.out.println(list.toArray(ss));
输出为: [Ljava.lang.String;@3ffc5af1
@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;
}
15 . 排序 sort(Comparator<? super E> c)
源码注释: 使用提供的 Comparator对此列表进行排序以比较元素。
源码分析: 从源码看, 排序也会导致并发修改的异常
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
sort使用方法:
创建一个比较器, Comparator
public interface Comparator<T> {
// 比较它的两个参数的顺序。返回负整数,零或正整数,
// 因为第一个参数小于,等于或大于第二个参数。
// 按由小到大的排序, 升序
int compare(T o1, T o2);
}
// 匿名内部类用法
arrayList.sort(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
if (o1 == null || o2 == null) {
return -1;
}
return o1.compareTo(o2);
}
});
// lambad 表达式用法
arrayList.sort((o1, o2)-> o1.compareTo(o2));
// 自定义类型用法
private static class Foo {
private int id;
private String name;
private void test() {
List<Foo> list = new ArrayList<TestList.Foo>();
list.add(new Foo());
list.sort((f1, f2)-> f1.id - f2.id);
}
}
16. 迭代 forEach(Consumer<? super E> action)
源码注释: 对Iterable的每个元素执行给定的操作,直到处理完所有元素或操作抛出异常为止。 除非实现类另有指定,否则操作按迭代顺序执行(如果指定了迭代顺序)。 操作抛出的异常将转发给调用者。
源码解析: 从这个方法的代码看,如果调用这个方法时,并发修改会抛出异常ConcurrentModificationException
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
使用方式:
// 匿名内部类形式
list.forEach(new Consumer<Foo>() {
@Override
public void accept(Foo foo) {
System.out.println(foo);
}
});
// lambda 方式
list.forEach((foo)-> System.out.println(foo)) ;
ArrayList 的克隆与序列化
a)克隆 clone()
源码注释: 返回此ArrayList实例的浅表副本。 (元素本身不会被复制。)
源码分析: (浅拷贝) 正如注释所描述的一样, 返回的 list副本里的元素是原来元素的指针, 操作此处的元素会导致源list里的元素发生改变.
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);
}
}
b)序列化 writeObject()
源码注释: 将ArrayList实例的状态保存到流(即序列化它)。
源码分析: 此处 s.writeObject(elementData[i]); 的作用是将实际元素的数量写到流中, 在 反序列化时,重新分配数组elementData 的大小;
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
c)反序列化 readObject()
源码注释: 从流中重构ArrayList实例(即反序列化它)。
源码分析: elementData = EMPTY_ELEMENTDATA 和 ensureCapacityInternal(size); 的调用就是为了重新分配数组的大小, 大小为当前list 实际 size 的大小;
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
ensureCapacityInternal(size);
Object[] a = elementData;
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
a[i] = s.readObject();
}
}
}
四、迭代器 Iterator
返回迭代器的方法有三个,iterator只支持向前遍历, listIterator 支持向前向后遍历
- iterator()
- listIterator()
- listIterator(int index)
1) 获取迭代器对象iterator()
public Iterator<E> iterator() {
return new Itr();
}
2) 获取列表迭代器 listIterator()
public ListIterator<E> listIterator() {
return new ListItr(0);
}
3) 获取从指定位置开始的列表迭代器 listIterator(index)
public ListIterator<E> listIterator(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}
ArrayList 里有两个内部类 Itr 和 ListItr
类图:
4) Itr 实现 Iterator 源码分析
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
.......... 忽略下面代码
}
字段说明:
// 下一元素的索引, 当迭代时, 执行一次 next()方法 cursor++,
// 当 cursor == size 和列表长度相等时即没有下一个元素;
int cursor;
// 最后一个元素的索引, 调用 next()方法时被重新赋值 lastRet = i;
int lastRet = -1;
// 因为ArrayList 是非线程安全的, 如果在迭代时列表被修改过,
// 即 expectedModCount != modCount; 马上抛出异常 ConcurrentModificationException
int expectedModCount = modCount;
是否有下一个元素hasNext():
源码分析: 下一元素索引小于列表的长度返回true
public boolean hasNext() {
return cursor != size;
}
获取下一个元素 next()
源码分析: 获取元素前检查列表是否被修改过, 然后cursor +1, lastRet = cursor,最后返回元素
public E next() {
checkForComodification();
int i = cursor; // 初始化为1, i = 1,
// 如果没有调用 hasNext()方法直接调用next()方法获取元素有可能导致索引越界的异常
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
// 检查列表是否被修改过, add、set、remove 等操作都会导致modCount的值发生改变
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
移除元素 remove()
源码注释: 从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。
源码解析: 在执行一次 next()方法后, 方可执行 remove(), 否则抛出异常IllegalStateException,
执行一次后, cursor 被修改, lastRet 重新赋 -1, expectedModCount = modCount;
所以在迭代列表时,只能使用迭代器的 remove()对列表删除元素
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
5) ListItr 源码实现
ListItr 继承了 Itr 实现 ListIterator, 并且有一个带参数的构造方法;
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<E> {
ListItr(int index) {
super();
cursor = index;
}
......忽略
}
这里只分析ListIterator 特有的方法
这里 cursor 直接继承自 Itr的属性, 作用是列表的索引;
// 判断是否有前一个元素
public boolean hasPrevious() {
return cursor != 0;
}
// 获取下一个索引
public int nextIndex() {
return cursor;
}
// 获取前一个元素索引
public int previousIndex() {
return cursor - 1;
}
获取前一个元素 previous()
源码注释: 返回列表中的前一个元素。
源码分析: 在使用时获取前一个元素,必须先执行hasPrevious() 方法判断是否有下一个元素;
在调用previous() 方法时,先查列表是否被修改过, 然后索引cursor - 1,
最终索引值被 -1, 元素被取出
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];
}
修改了列表指定索引的元素 set(E e)
源码注释: 用指定元素替换 next 或 previous 返回的最后一个元素(可选操作)。
源码分析: 从代码逻辑看, 此处修改的值为最后一次调用previous()获取的那个值,
此处的操作直接改变列表指定的值
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.set(lastRet, e);
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
往列表添加一个元素 add(E e)
源码注释: 将指定的元素插入列表(可选操作)。
源码分析: 从源码的逻辑看, 此处添加的元素到列表指定索引处的位置, 这个索引处就是最后一次调用 previous() 方法后返回的元素的索引位置的左边
public void add(E e) {
checkForComodification();
try {
int i = cursor;
ArrayList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
扩展阅读
ArrayList 实现 | ArrayList 列表实现浅析 |
LinkedList 实现 | LinkedList 双链表实现浅析 |
2018年12月11日
持续更新…