欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 新车 > 理解List AbstractList ArrayList

理解List AbstractList ArrayList

2025/11/5 3:29:53 来源:https://blog.csdn.net/qq_54867493/article/details/141424744  浏览:    关键词:理解List AbstractList ArrayList

ArrayList 实现了 List 接口,继承了 AbstractList 抽象类。

Q: 为什么要ArrayList继承AbstractList,让AbstractList实现List?而不是让ArrayList直接实现List?

A: 接口中全都是抽象的方法,而抽象类中可以有抽象方法,还可以有具体的实现方法。因此让AbstractList实现接口中一些通用的方法,而具体的类(如ArrayList)继承AbstractList类获得通用的方法,再实现一些自己特有的方法,使代码更简洁。

索引和游标的关系示意图,帮助理解后续源码。

AbstractList实现了List中的部分方法,剩余的方法被转为 abstract 方法,由 AbstractList 的子类(如ArrayList, LinkedList 和 Vector)实现。

// Search Operations// 1.获取某个元素在集合中的索引
public int indexOf(Object o) {// AbstractList内部提供了Iterator, ListIterator迭代器的实现类,分别为Itr,ListItrListIterator<E> it = listIterator();if (o==null) {while (it.hasNext())if (it.next()==null)return it.previousIndex();} else {while (it.hasNext())if (o.equals(it.next()))return it.previousIndex();}//如果集合中不存在该元素,返回-1return -1;
}// 2.获取某个元素在集合中最后一次出现的索引
public int lastIndexOf(Object o) {ListIterator<E> it = listIterator(size());if (o==null) {while (it.hasPrevious())if (it.previous()==null)return it.nextIndex();} else {while (it.hasPrevious())if (o.equals(it.previous()))return it.nextIndex();}return -1;
}// Iterators// 1.获取Iterator接口Itr实现类迭代器
public Iterator<E> iterator() {return new Itr();
}// 2.获取从0开始(初始位置)的ListIterator的实现类ListItr
public ListIterator<E> listIterator() {return listIterator(0);
}// 3.获取从索引等于index的位置的迭代器
public ListIterator<E> listIterator(final int index) {// 检查下标合法性rangeCheckForAdd(index);return new ListItr(index);
}// 内部实现了Iterator接口的实现类Itr
private class Itr implements Iterator<E> {// 游标标识int cursor = 0;// 上一次迭代到的元素的光标位置int lastRet = -1;// 结构修改计数器。如果两个值不一致,说明发生了并发操作,就会报错int expectedModCount = modCount;public boolean hasNext() {return cursor != size();}// 获取下一个元素public E next() {// 判断是否有并发操作checkForComodification();try {int i = cursor;E next = get(i);lastRet = i;cursor = i + 1;return next;} catch (IndexOutOfBoundsException e) {checkForComodification();throw new NoSuchElementException();}}// 删除上一次迭代器越过的元素public void remove() {if (lastRet < 0)throw new IllegalStateException();checkForComodification();try {// 调用需要子类去实现的remove方法AbstractList.this.remove(lastRet);if (lastRet < cursor)cursor--;// 每次删除后,将lastRet置为-1,防止连续的删除lastRet = -1;// 更新 expectedModCount 确保下次迭代时能通过并发检查expectedModCount = modCount;} catch (IndexOutOfBoundsException e) {throw new ConcurrentModificationException();}}/**AbstractList 内部获取迭代器。迭代过程使用迭代器本身的 remove() 移除元素是被允许的,迭代器会更新 expectedModCount,保证下一次的 next() 通过 checkForComodificatio() 检查。“用户”在迭代过程中调用 public 的会导致数组结构性修改的方法(add、remove)是不被允许的。**/final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}
}// 继承自Itr的ListIterator的实现类ListItr
private class ListItr extends Itr implements ListIterator<E> {// 指定光标位置等于索引的迭代器构造ListItr(int index) {cursor = index;}public boolean hasPrevious() {return cursor != 0;}public E previous() {checkForComodification();try {// 注意索引和游标的区别int i = cursor - 1;E previous = get(i);lastRet = cursor = i;return previous;} catch (IndexOutOfBoundsException e) {checkForComodification();throw new NoSuchElementException();}}// 下一位的索引值等于光标值public int nextIndex() {return cursor;}// 上一位的索引值等于光标值减一public int previousIndex() {return cursor-1;}// 设置元素public void set(E e) {if (lastRet < 0)throw new IllegalStateException();checkForComodification();try {// 调用需要子类去实现的 set 方法AbstractList.this.set(lastRet, e);expectedModCount = modCount;} catch (IndexOutOfBoundsException ex) {throw new ConcurrentModificationException();}}// 添加元素public void add(E e) {checkForComodification();try {// 设置添加的位置为当前光标所在的位置int i = cursor;AbstractList.this.add(i, e);// 添加的元素不允许立即删除lastRet = -1;cursor = i + 1;expectedModCount = modCount;} catch (IndexOutOfBoundsException ex) {throw new ConcurrentModificationException();}}
}

其实这里我在分析的时候是存在疑问的(为什么 Itr 中的 next() 方法的 cursor 实际上等于 lastRet + 1,而 previous() 方法中 lastRet = cursor),请看图解。

lastRet代表上一次迭代到的元素的光标位置,在next操作中,上一次迭代的元素为"E",对应光标位置为4;在previous操作中,上一次迭代的元素为"D",对应光标位置为3。

我们根据以上代码也可以解释阿里巴巴开发手册的这一点:

由于checkForComodification,“用户”在迭代过程中调用 public 的会导致数组结构性修改的方法(add、remove)是不被允许的。而 Iterator 的 remove 方法在删除完会执行 expectedModCount = modCount,保证了 expectedModCount 与 modCount 的同步。

// 如何正确地删除元素
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");// 反例,使用for-each会报错
for (String str : list) {if ("B".equals(str)) {list.remove(str);}
}// 反例,使用for循环
// 初始list.size()=3,当i=1时,执行remove后list.size=2,意味着“C”元素被跳过了
for (int i = 0; i < list.size(); i++) {String str = list.get(i);if ("B".equals(str)) {list.remove(str);}
}// 正例
Iterator<String> itr = list.iterator();
while (itr.hasNext()) {String str = itr.next();if ("B".equals(str)) {itr.remove();}
}

除此之外,在 AbstractList 里面有一个很有意思的equals 方法

public boolean equals(Object o) {if (o == this)return true;if (!(o instanceof List))return false;ListIterator<E> e1 = listIterator();ListIterator<?> e2 = ((List<?>) o).listIterator();while (e1.hasNext() && e2.hasNext()) {E o1 = e1.next();Object o2 = e2.next();if (!(o1==null ? o2==null : o1.equals(o2)))return false;}return !(e1.hasNext() || e2.hasNext());
}

在第二步检查时,判断了是否为实现了 List 的类的实例。要知道 List 是有很多实现类的,它们都能通过第二步判断,而且同时也都能通过 ListIterator 获取迭代器进行遍历。说明不同的 List 实现类只要装载的内容相同,那么通过 equals 判断的结果就为 true

public static void main(String[] args) {ArrayList<String> arrayList = new ArrayList<>();arrayList.add("peterxx");LinkedList<String> linkedList = new LinkedList<>();linkedList.add("peterxx");// 输出为TrueSystem.out.println(arrayList.equals(linkedList));
}

只要是同一类型的容器(都实现了 List),并且所有元素相同,那么两者就是相等,无需关心容器的实现细节差别(如 ArrayList 与 LinkedList

参考:ArrayList和LinkedList的区别:如何选择? | 二哥的Java进阶之路 (javabetter.cn) 

Java基础系列(四十二):集合之AbstractList - 《山禾说Java》 - 极客文档 (geekdaxue.co)

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词