ArrayList与LinkedList简洁对比:
ArrayList是查询速度比较快,插(插入中间,而不是末尾)删速度比较慢,而LinkedList就是查询速度比较慢,插(插入中间)删速度比较快,到底是什么原因导致了它们的不一样呢?了解过它们的底层实现,就可以知道,ArrayList底层是由数组实现的,数组就是在内存里面申请一段连续的内存,然后在里面读写数据,它查询的时间复杂度是O(1),但是如果要往数组里面添加或者删除数据的话,在添加(删除)位置后面数都要往前或者往后相应的位置,所以它增删的时间复杂度是O(n)。然而,LinkedList的底层是由链表实现的,而链表查询数据的时间复杂度是O(n),而增删数据的时间复杂度是O(1)。由此可见,它们在查询和增删数据两个维度上有着不同的差异,所以在具体使用时,可以根据自己的需要选择不同的数据结构。
如果仅仅是简单的放进去,然后取出来,用ArrayList,效率更高一些!
//源码中LinkedList类的声明
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
源码注解:
⑴、LinkedList(双链列表)同时实现了接口List和Deque(LinkedList实现了这两个接口的所有方法),它既是一个链表,也是一个双端队列。它允许添加任何元素,包括null元素。
⑵、LinkedList所有方法的实现都体现了双链表的特性,所有有关索引的操作都自动从LinkedList的开头或结尾开始遍历(如果索引更接近开头,自动从开头开始遍历,更接近结尾,自动从结尾开始遍历)。
⑶、LinkedList不是线程安全的,如果在多线程环境中涉及结构修改(结构修改是指添加或删除一个或多个元素),必须从外部保证LinkedList的线程安全(通过对持有LinkedList的对象进行同步来完成)。如果不存在持有LinkedList的对象,应该使用类Collections的方法synchronizedList(new LinkedList(…)),具体的代码如下:
List list = Collections.synchronizedList(new LinkedList(...));
最好在创建LinkedList对象时就这么做,以防止任何不安全的操作发生。
⑷、在获取了迭代器或列表迭代器后,除非使用它们自身提供的方法(方法remove()和add())修改LinkedList的结构,否则都将抛出ConcurrentModificationException异常(快速失败机制)。迭代器不会在未来的不确定时间内冒任意不确定行为的风险(如果在多线程环境的迭代过程中修改了LinkedList的结构,可能返回很多种结果)。
⑸、注意,无法保证快速失败机制一定会执行,只能说迭代器会尽最大的努力抛出ConcurrentModificationException异常。因此,通过快速失败机制保证程序的正确性是错误的,它通常只能用于检测错误。
节点的图示:
LinkedList中的每个节点都是一个单独的存储空间,”元素”存储在节点中,但节点中不仅只有元素。ArrayList的底层是数组,数组是连续的存储空间(通过持有数组的引用+”[ 索引 ]”+访问),所以每个存储空间只需存储元素,不需要存储相互之间的关系。而LinkedList底层是链表(双向),链表不是连续的存储空间,所以每个存储空间(节点)不仅需要存储元素,还需要存储相互之间的关系(prev:上一个节点的位置,如果有。next:下一个节点的位置,如果有)。
链表的图示:
在链表中,一个独立的存储空间就是一个节点,同时,一个节点就是一个内部类Node的对象实例。链表是由若干个节点构成的,
内部类Node的源码:
//链表节点的类型
//一个对象对应一个节点
private static class Node<E> {
//元素的引用
//如果为null,表示没有存储任何元素,如果不为null,表示存储了某种类型的元素
E item;
//下一个节点的引用
//引用代表了对象的十六进制地址值,所以也可以注释为:下一个节点在内存中的地址
//如果为null,可能是空链表,也可能是尾节点
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
//元素的引用初始化
this.item = element;
//上一个节点的引用初始化
this.next = next;
//下一个节点的引用初始化
this.prev = prev;
}
}
变量
size
//元素的数量,初始化为0
//关键字transient表示它不能被序列化
transient int size = 0;
first
//首节点的固定引用(方便节点的创建、添加和链表的查找遍历)
//链表必须先创建添加首节点,才能创建添加第二个节点,以此类推
//查找遍历同理,链表是顺序访问列表,只能从开头或者末尾开始挨个访问
//所以首节点不可能匿名,需要一个固定引用方便后续的使用
//关键字transient表示它不能被序列化
transient Node<E> first;
last
//尾节点的固定引用(方便节点的查找遍历)
//关键字transient表示它不能被序列化
transient Node<E> last;
构造器
LinkedList()
//构造方法,创建一个没有任何节点的空LinkedList
public LinkedList() {}
LinkedList(Collection<? extends E> c)
//构造方法,创建这样一个LinkedList
//①、节点数量与指定集合c的元素数量一致
//②、指定集合c中的每个元素都存储在对应索引值的节点中
//换句话说就是:节点的顺序与指定集合c的元素顺序完全一致
public LinkedList(Collection<? extends E> c) {
//调用无参构造函数,如果它在后期的Java
//升级版本中发生修改,可以实时与它保持一致
//保证了程序的可扩展性,方便了后期的代码维护
this();
//通过方法addAll(Collection<? extends E> c)
//添加c的所有元素
addAll(c);
}
linkFirst(E e)
//创建一个包含元素e的新节点
//并链接至链表的首部(linkFirst),这个节点就是链表的新首节点
//方法存在的意义:LinkedList同时implement了接口Queue和Deque
private void linkFirst(E e) {
//假设链表不为空,获取并传递首节点的引用
//传递后,引用f和first同时指向首节点
//这一步实际上也是复制首节点的引用
final Node<E> f = first;
//分别提供以下参数创建一个节点对象newNode:
//①、上一个节点的引用null
//②、元素e
//③、下一个节点的引用f
//newNode其实就是新的首节点,两点原因:
//①、在链表中,只有首节点的上一个节点引用为null
//②、原首节点f成为newNode的下一个节点
//③、节点关系的维护放在了构造函数中(参考截图)
final Node<E> newNode = new Node<>(null, e, f);
//将newNode赋值给first,保证first永远都是首节点的引用
first = newNode;
//上面的代码只初始化了first,并未初始化last
//如果首节点f为null,说明链表为空
//所以新创建的节点newNode既是首节点也是尾节点
if (f == null)
//初始化last
last = newNode;
else
//如果首节点f不为空
//需要考虑两方面:元素和关系
//元素由客户端程序员添加
//这里只需考虑关系
//首节点f的prev肯定为null
//现在创建了新的首节点newNode
//f自然就是第二节点
//所以需要通过代码表示:
//f的上一个节点是newNode
//以此保证各个节点的关系正确
f.prev = newNode;
//新增一个节点,长度+1
size++;
//继承自抽象类AbstractList,保证快速失败机制的正常执行
//新增节点是结构性修改
modCount++;
}
linkLast(E e)
//创建一个包含元素e的新节点,并链接至链表的尾部(linkLast)
//这个节点就是链表的新尾节点
//方法存在的意义:LinkedList同时implement了接口Queue和Deque
//实现它们的方法时可以有效避免代码重复
void linkLast(E e) {
//假设链表不为空,获取并传递尾节点的引用
//传递后,引用l和last同时指向尾节点
//这一步实际上也是复制尾节点的引用
final Node<E> l = last;
//创建一个节点对象newNode://①、上一个节点的引用l②、元素e
//③、下一个节点的引用null
//newNode其实就是新的尾节点,两点原因:
//①、在链表中,只有尾节点的下一个节点引用为null
//②、原尾节点l成为newNode的上一个节点
final Node<E> newNode = new Node<>(l, e, null);
//将newNode赋值给last,保证last永远都是尾节点的引用
last = newNode;
//上面的代码只初始化了last,并未初始化first
//如果尾节点l为null,说明链表为空
//所以新创建的节点newNode既是尾节点也是首节点
if (l == null)
//初始化first
first = newNode;
else
//如果尾节点l不为空
//需要考虑两方面:元素和关系
//元素由客户端程序员添加
//这里只需考虑关系
//尾节点l的next肯定为null
//现在创建了新的尾节点newNode
//l自然就是倒数第二节点
//所以需要通过代码表示:
//l的下一个节点是newNode
//以此保证各个节点的关系正确
l.next = newNode;
//新增一个节点,长度+1
size++;
//继承自抽象类AbstractList,保证快速失败机制的正常执行
modCount++;
}
linkBefore(E e, Node< E > succ)
//创建一个包含指定元素e的节点
//并将它链接在指定节点succ之前(linkBefore)
void linkBefore(E e, Node<E> succ) {
//获取节点succ的上一个节点
final Node<E> pred = succ.prev;
//分别提供以下参数创建一个节点对象newNode:
//①、上一个节点的引用pred
//②、元素e
//③、下一个节点的引用succ
final Node<E> newNode = new Node<>(pred, e, succ);
//创建后还需要维护节点之间的关系
//以保证newNode是succ的上一个节点
succ.prev = newNode;
//---------------下面需要考虑一些其它情况
//如果pred为null,说明succ是链表的首节点
//(只有首节点的上一个节点为null)
//既然是首节点,那么就还有另一个固定的引用first
if (pred == null)
//newNode代替了succ成为新的首节点
//需要对first重新进行初始化,保证first永远都是首节点的引用
first = newNode;
//如果pred不为null
//上面的代码只维护了newNode → succ方向的关系
//(succ.prev = newNode)
//LinkedList是双向的
//所以,还需要维护pred → newNode方向的关系
else
//newNode是pred的下一个节点
pred.next = newNode;
//新增一个节点,长度+1
size++;
//继承自抽象类AbstractList,保证快速失败机制的正常执行
//新增节点是结构性修改
modCount++;
}
unlinkFirst(Node< E > f)
//在指定节点f处将链表一分为二
//链表左边直接丢弃(包含节点f)
//链表右边是新的链表
private E unlinkFirst(Node<E> f) {
//获取节点f的元素
final E element = f.item;
//获取节点f的下一个节点
final Node<E> next = f.next;
//清空节点f的元素
f.item = null;
//断开节点f与下一个节点的关系
//在这里将链表一分为二
//链表左边直接丢弃(包含节点f)
f.next = null;
//链表右边的首节点
//也就是节点f的下一个节点next
//成为新链表的首节点
//需要对first重新进行初始化
//保证first永远都是首节点的引用
first = next;
//---------------下面需要考虑一些其它情况
//如果next为null,说明新链表为null
if (next == null)
//那么新链表的尾节点也是null
//需要对last重新初始化
last = null;
//如果next不为null,它就是新链表的首节点
//上面的代码只断开了f → next方向的关系
//(f.next = null)
//LinkedList是双向的
//所以,还需要断开next → f方向的关系
else
next.prev = null;
//同时,首节点的上一个节点也应该是null
//删除一个节点,长度-1
size--;
//继承自抽象类AbstractList,保证快速失败机制的正常执行
//删除节点是结构性修改
modCount++;
return element;
}
unlinkLast(Node< E > l)
//在指定节点l处将链表一分为二
//链表右边直接丢弃(包含节点l)
//链表左边是新的链表
private E unlinkLast(Node<E> l) {
//获取节点l的元素
final E element = l.item;
//获取节点l的上一个节点
final Node<E> prev = l.prev;
//清空节点l的元素
l.item = null;
//断开节点l与上一个节点的关系
//在这里将链表一分为二
//链表右边直接丢弃(包含节点l)
l.prev = null;
//链表左边的尾节点
//也就是节点l的上一个节点prev
//成为新链表的尾节点
//需要对last重新进行初始化
//保证last永远都是尾节点的引用
last = prev;
//---------------下面需要考虑一些其它情况
//如果prev为null,说明新链表为null
if (prev == null)
//那么新链表的首节点也是null
//需要对first重新初始化
first = null;
//如果prev不为null,它就是新链表的尾节点
//上面的代码只断开了l → prev方向的关系
//(l.prev = null)
//LinkedList是双向的
//所以,还需要断开prev → l方向的关系
else
prev.next = null;
//同时,尾节点的下一个节点也应该是null
//删除一个节点,长度-1
size--;
//继承自抽象类AbstractList,保证快速失败机制的正常执行
//删除节点是结构性修改
modCount++;
return element;
}
unlink(Node< E > x)
//删除指定节点x
E unlink(Node<E> x) {
//获取节点x的元素
final E element = x.item;
//获取节点x的下一个节点
final Node<E> next = x.next;
//获取节点x的上一个节点
final Node<E> prev = x.prev;
//如果prev为null,说明x是首节点
//删除x后,它的下一个节点(next)就自动成为新的首节点
if (prev == null) {
//需要对first重新进行初始化,保证first永远都是首节点的引用
first = next;
} else {
prev.next = next;
x.prev = null;
}
//如果next为null,说明x是尾节点
//删除x后,它的上一个节点(next)就自动成为新的尾节点
if (next == null) {
//需要对last重新进行初始化,保证last永远都是尾节点的引用
last = prev;
//如果next不为null,只需要维护节点之间的关系
//删除x,那么它的下一个节点(next.prev)
//和上一个节点(prev)将直接产生联系
} else {
next.prev = prev;
//linkedList是双向的
//上面的代码只断开了x → prev方向的关系
//还需要断开x → next方向的关系
x.next = null;
}
//清空节点x的元素
x.item = null;
//删除一个节点,长度-1
size--;
//继承自抽象类AbstractList,保证快速失败机制的正常执行
//删除节点是结构性修改
modCount++;
return element;
}
以上都是LinkedList的内部支撑方法,下面将要介绍LinkedList对外提供的方法,有以下几类:接口Deque的方法、接口Queue的方法、栈的方法等。
Deque接口方法(双端队列)
Deque的特点是既可以在首部也可以在尾部添加和删除节点。
getFirst()
//返回首节点的元素
public E getFirst() {
//获取首节点
final Node<E> f = first;
//如果首节点为null
if (f == null)
//抛出NoSuchElementException异常
throw new NoSuchElementException();
//返回首节点的元素
return f.item;
}
getLast()
//返回尾节点的元素
public E getLast() {
//获取尾节点
final Node<E> l = last;
//如果尾节点为null
if (l == null)
//抛出NoSuchElementException异常
throw new NoSuchElementException();
//返回尾节点的元素
return l.item;
}
removeFirst()
//从链表的首部删除节点
public E removeFirst() {
//获取首节点
final Node<E> f = first;
//如果首节点为null
if (f == null)
//抛出NoSuchElementException异常
throw new NoSuchElementException();
//参考方法unlinkFirst(Node< E > f)
return unlinkFirst(f);
//在方法unlinkFirst(f)中,维护了删除操作发生之后
//剩下的节点之间的关系(first会被重新初始化)
//所以f总是新的首节点
}
removeLast()
//从链表的尾部删除节点
public E removeLast() {
//获取尾节点
final Node<E> l = last;
//如果尾节点为null
if (l == null)
//抛出NoSuchElementException异常
throw new NoSuchElementException();
//参考方法unlinkLast(Node< E > f)
return unlinkLast(l);
//在方法unlinkLast(l)中,维护了删除操作发生之后
//剩下的节点之间的关系(last会被重新初始化)
//所以l总是新的尾节点
}
addFirst(E e)
//在链表的首部添加节点
public void addFirst(E e) {
//参考方法linkFirst(E e)
linkFirst(e);
//在方法linkFirst(e)中,维护了添加操作发生之后
//添加的节点和原有节点之间的关系
//实际上,添加的节点就是新的首节点
//(first会被重新初始化)
}
addLast(E e)
//在链表的尾部添加节点
public void addLast(E e) {
//参考方法linkLast(E e)
linkLast(e);
//在方法linkLast(e)中,维护了添加操作发生之后
//添加的节点和原有节点之间的关系
//实际上,添加的节点就是新的尾节点
//(last会被重新初始化)
}
contains(Object o)
//判断链表中是否存在包含指定元素o的节点
public boolean contains(Object o) {
//如果方法indexOf(o)的返回值为-1,则返回true,否则返回false
return indexOf(o) != -1;
}
接口Queue的方法
Queue的特点是只能在尾部添加元素,只能在首部删除元素,归纳起来就是”先进先出”.
add(E e)
//参考方法addLast(E e)
//在链表的尾部添加元素
public boolean add(E e) {
//参考方法linkLast()
linkLast(e);
return true;
}
remove(Object o)
//从左至右遍历链表,删除元素与
//指定元素o相等的第一个节点
public boolean remove(Object o) {
//如果o为null
if (o == null) {
//通过for循环从左至右遍历链表,x为循环因子
//x的初始化值为首节点first,只要x不为null
//就继续将下一个节点赋值给x(步进)
//第一次循环结束后,x从首节点变成了第二节点
//以此类推,直到循环到尾节点
for (Node<E> x = first; x != null; x = x.next) {
//如果节点元素x.item为null
if (x.item == null) {
//删除节点
unlink(x);
return true;
}
}
//如果o不为null
} else {
//同上
for (Node<E> x = first; x != null; x = x.next) {
//如果节点元素x.item与指定元素o相等
if (o.equals(x.item)) {
//删除节点
unlink(x);
return true;
}
}
}
//如果链表为空或链表不包含
//指定元素o,直接终止程序返回false
return false;
}
List接口方法
get(int index)
//返回指定索引为index的节点的元素
public E get(int index) {
//检查索引值index是否有效
//如果无效直接抛出异常
checkElementIndex(index);
//参考方法node(int index)
//返回节点的元素
return node(index).item;
}
set(int index, E element)
//用指定元素element代替索引值为index的节点的元素
public E set(int index, E element) {
//检查索引值index是否有效
//如果无效直接抛出异常
checkElementIndex(index);
//参考方法node(int index)
//获取索引值为index的节点
Node<E> x = node(index);
//获取节点的元素oldVal
E oldVal = x.item;
//给节点的元素重新赋值element
x.item = element;
//返回元素oldVal
return oldVal;
}
add(int index, E element)
//将包含指定元素element的节点
//添加在链表的指定索引位置index
public void add(int index, E element) {
//检查索引值index是否有效
//如果无效直接抛出异常
checkPositionIndex(index);
//根据索引值index判断添加节点的位置
//在链表的尾部添加
if (index == size)
//参考方法linkLast(int index)
linkLast(element);
//在链表的中间添加
else
//参考方法node(int index)
//参考方法linkBefore(E e, Node< E > succ)
//先获取索引值为index的节点
//再将它作为实际参数传入方法
//linkBefore(E e, Node< E > succ)中
linkBefore(element, node(index));
}
remove(int index)
//删除指定索引值是index的节点
public E remove(int index) {
//检查索引值index是否有效
//如果无效直接抛出异常
checkElementIndex(index);
//参考方法node(int index)
//参考方法unlink(Node< E > x)
//先获取索引值为index的节点
//再将它作为实际参数传入方法
//unlink(Node< E > x)中
return unlink(node(index));
}
node(int index)
//返回指定索引值为index的非空节点。
Node<E> node(int index) {
//这里涉及到一个提高效率的操作
//因为LinkedList是双向的,既可以
//从首部开始,也可以从尾部开始
//所以以链表长度的一半为标准,如果index
//大于它,则从尾部开始,如果index小于它
//则从首部开始
//考虑index小于链表一半的情况
//size >> 1 等价于size/2
if (index < (size >> 1)) {
//获取首节点,从它开始
//方向是从左至右
Node<E> x = first;
//for循环
for (int i = 0; i < index; i++)
//步进表达式
x = x.next;
//从上面的循环中就能看出链表
//顺序访问的特点,循环中没有任何
//的判断或者条件语句,只是单纯的循环
//从首节点first开始,经过index-2个
//节点后才到达目标节点
//返回目标节点x
return x;
//考虑index大于链表一半的情况
} else {
//获取尾节点,从它开始
//方向是从右至左
Node<E> x = last;
for (int i = size - 1; i > index; i--)
//步进表达式
x = x.prev;
//返回目标节点x
return x;
}
}
indexOf(Object o)
//返回元素与指定元素o相等的
//第一个节点的索引值
//从首节点开始遍历链表,方向为从左至右
public int indexOf(Object o) {
//链表是顺序访问的
//第一个节点的索引值为0
int index = 0;
//考虑指定元素o为null的情况
if (o == null) {
//for循环遍历链表
//遍历方向为从左至右
for (Node<E> x = first; x != null; x = x.next) {
//如果元素为null
if (x.item == null)
//返回索引值
return index;
//如果元素不为null
//自动递增索引值
index++;
}
//考虑指定元素o不为null的情况
} else {
//for循环遍历链表
//遍历方向为从左至右
for (Node<E> x = first; x != null; x = x.next) {
//如果元素与指定元素o相等
if (o.equals(x.item))
//返回索引值
return index;
//如果元素与指定元素o不相等
//自动递增索引值
index++;
}
}
//如果链表不包含指定元素o,返回-1
return -1;
}
lastIndexOf(Object o)
//返回元素与指定元素o相等的
//第一个节点的索引值
//从尾节点开始遍历链表,方向为从右至左
public int lastIndexOf(Object o) {
//链表是顺序访问的
//第一个节点的索引值为size
int index = size;
//考虑指定元素o为null的情况
if (o == null) {
//for循环遍历链表
//遍历方向为从右至左
for (Node<E> x = last; x != null; x = x.prev) {
//注意与方法indexOf(Object o)的区别
//lastIndexOf(Object o)先递减索引值index
//再进行判断
index--;
//如果元素为null
if (x.item == null)
//返回索引值index
return index;
}
//考虑指定元素o不为null的情况
} else {
//for循环遍历链表
//遍历方向为从右至左
for (Node<E> x = last; x != null; x = x.prev) {
//先递减索引值index
index--;
//如果元素与指定元素o相等
if (o.equals(x.item))
//返回索引值index
return index;
}
}
//如果链表不包含指定元素o,返回-1
return -1;
}
类Stack的方法
Queue的特点是只能在尾部添加元素,只能在首部删除元素,归纳起来就是”后进先出”
peek()
//返回链表首节点的元素
//如果首节点为null直接返回null
public E peek() {
final Node<E> f = first;
//如果f为null,则返回null,否则返回元素
return (f == null) ? null : f.item;
}
element()
//返回链表首节点的元素
//如果首节点为null直接
//抛出NoSuchElementException异常
public E element() {
//参考方法getFirst()
return getFirst();
}
poll()
//返回链表首节点的元素后删除首节点
//如果首节点为null直接返回null
public E poll() {
final Node<E> f = first;
//如果f为null,返回null
//否则返回方法unlinkFirst(f)的结果
//参考方法unlinkFirst(Node< E > f)
return (f == null) ? null : unlinkFirst(f);
}
remove()
//在链表的首部删除元素
//如果首节点为null直接
//抛出NoSuchElementException异常
public E remove() {
//参考方法removeFirst()
return removeFirst();
}
offer(E e)
//在链表的尾部添加节点
public boolean offer(E e) {
//参考方法add(E e)
return add(e);
}
offerFirst(E e)
//在链表的首部添加节点
public boolean offerFirst(E e) {
//参考方法addFirst(E e)
addFirst(e);
return true;
}
offerLast(E e)
//在链表的尾部添加节点
public boolean offerLast(E e) {
//参考方法addFirst(E e)
addLast(e);
return true;
}
peekFirst()
//返回链表首节点的元素
//如果首节点为null直接返回null
public E peekFirst() {
final Node<E> f = first;
//如果f为null,则返回null,否则返回元素
return (f == null) ? null : f.item;
}
peekLast()
//返回链表尾节点的元素
//如果首节点为null直接返回null
public E peekLast() {
final Node<E> l = last;
//如果f为null,则返回null,否则返回元素
return (l == null) ? null : l.item;
}
pollFirst()
//返回链表首节点的元素后删除首节点
//如果首节点为null直接返回null
public E pollFirst() {
final Node<E> f = first;
//如果f为null,返回null
//否则返回方法unlinkFirst(f)的结果
//参考方法unlinkFirst(Node< E > f)
return (f == null) ? null : unlinkFirst(f);
}
pollLast()
//返回链表尾节点的元素后删除尾节点
//如果首节点为null直接返回null
public E pollLast() {
final Node<E> l = last;
//如果f为null,返回null
//否则返回方法unlinkLast(f)的结果
//参考方法unlinkLast(Node< E > f)
return (l == null) ? null : unlinkLast(l);
}
push(E e)
//在链表的首部添加节点
public void push(E e) {
//参考方法addFirst(E e)
addFirst(e);
}
pop()
//在链表的首部删除元素
//如果首节点为null直接
//抛出NoSuchElementException异常
public E pop() {
//参考方法removeFirst()
return removeFirst();
}
listIterator(int index)
//返回从指定索引位置index开始的列表迭代器
public ListIterator<E> listIterator(int index) {
//检查索引值index是否有效
//如果无效直接抛出异常
checkPositionIndex(index);
//返回一个内部类ListItr对象
return new ListItr(index);
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/110915.html