功能:该类提供了List相关类/接口的基础框架
开始版本:JDK 1.2
注意:
1. 本类为抽象类
2. 可以存在相同的值,也可以为null
3. 数据结构:顺序表、链表
继承类:AbstractCollection
实现接口:List
所在包:java.util
导入类:无
类声明:
public abstract class AbstractListextends AbstractCollection implements List
备注:本方法没有单独重写toString()方法,使用的是AbstractCollection的toString()方法
框架图:
变量 保护变量 01.当前对象被修改的次数modCount// 修改次数:初始为0 protected transient int modCount = 0;方法 构造器
// 唯一构造器
protected AbstractList() {}
内部类
01.迭代器 Itr
注意:
1. 通过迭代器中的remove()方法,对象的变量modCount和迭代器内部的expectedModCount均会改变,同步了修改,因此不会抛出ConcurrentModificationException异常
2. 使用本类的移除有关方法(非迭代器方法),只会改变对象的变量modCount,不会改变内部变量expectedModCount,导致对象和迭代器未同步,会导致ConcurrentModificationException异常
3. lastRet变量为当前元素的前一个元素下标,迭代器的remove()方法移除的是前一个元素(即上一次调用next()获取的元素),删除后后面的元素全部前移了一位,因此需要将当前元素的游标会前移一位,否则就跳过了原先此下标的元素
4. 迭代器的remove()方法移除前一个元素,这样不会破坏原对象遍历结果,而删除后面元素会影响遍历结果,因此若遍历中进行了增删改,遍历出来的结果和实际对象中的元素并不一定一致
5. 迭代器的remove()不能连续调用两次,如果不禁止连续删除而是直接给lastRet和cursor减一原则上可以(开始仍然判断lastRet < 0),但会将已经遍历的元素删除容易使数据混乱,因此删除一次后会直接将lastRet会置为-1,而remove()方法开始就校验了lastRet不能小于0
// 源码 private class Itr implements Iterator{ // 游标后一个元素的下标 int cursor = 0; // 上一个使用next()获取的元素下标,若元素被删除,此值重置为-1 int lastRet = -1; // 当前集合被修改的次数 int expectedModCount = modCount; // 判断游标后是否还有元素 public boolean hasNext() { return cursor != size(); } // 返回当前游标后的元素,实际调用的是get()方法 public E next() { // 校验是否生成迭代器后有修改过值,若有则抛出ConcurrentModificationException异常 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() { // 比较了当前游标是否在第一个元素之前或刚进行过移除操作,是则直接报错(remove()方法移除的是当前游标前的元素) if (lastRet < 0) throw new IllegalStateException(); // 校验修改次数是否与生成迭代器一致,不一致抛出ConcurrentModificationException异常 checkForComodification(); try { // 调用集合的移除元素方法,移除的是当前游标前的元素 AbstractList.this.remove(lastRet); if (lastRet < cursor) cursor--; // 移除元素后此值设置为-1,只要调用了next()移动了游标此处就不再是-1,而连续删除不会修改lastRet,仍是-1 lastRet = -1; // 修改集合的修改次数并同步至迭代器(就是这里使得通过迭代器移除元素不会报错) expectedModCount = modCount; } catch (IndexOutOfBoundsException e) { throw new ConcurrentModificationException(); } } // 校验修改次数是否与生成迭代器一致,不一致则抛出ConcurrentModificationException异常 final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
// 错误示例1:游标未移动就删除元素
@Test
void contextLoads3() {
List list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
list.add("6");
list.add("7");
list.add("8");
list.add("9");
list.add("10");
Iterator it = list.iterator();
// 还未进行游标移动,游标前无元素,抛出java.lang.IllegalStateException异常
it.remove();
}
// 错误示例2:连续调用迭代器移除方法
@Test
void contextLoads() {
List list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
list.add("6");
list.add("7");
list.add("8");
list.add("9");
list.add("10");
Iterator it = list.iterator();
it.next();
it.next();
it.next();
it.next();
// 此处已设置lastRet为-1
it.remove();
// lastRet为-1,抛出java.lang.IllegalStateException异常java.lang.IllegalStateException
it.remove();
}
// 错误示例3:生成迭代器后调用对象的删除方法(增加方法一样)
@Test
void contextLoads() {
List list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
list.add("6");
list.add("7");
list.add("8");
list.add("9");
list.add("10");
Iterator it = list.iterator();
it.next();
list.remove(0);
// 抛出ConcurrentModificationException异常
it.next();
}
02.List迭代器 ListItr
注意:
1. 本迭代器继承于Iterator,单独为List集合提供了一些迭代器方法
2. 本迭代器并非游标固定在两端,而是将游标初始化在传输的集合下标前,传输0则从将游标放于开头
3. 除却迭代器通有的hasNext()、next()、remove()及一些校验方法,增加了适用于集合的hasPrevious、previous()、nextIndex()、previousIndex()、set()、add()方法
4. 集合原有的add()方法会使得modCount++,而迭代器增加的add()同步了修改次数,set()两者均未对modCount变量操作,效果一致,但若有些集合实现类对set()进行了修改使得modCount改变仍会对迭代器有影响,因此建议优先使用迭代器内的修改元素方法
5. set()和remove()方法均在开始时校验了lastRet变量不为-1,因此这两个操作中间必须进行next()操作,原因仍是连续操作会对已经遍历的元素产生影响容易使数据混乱,而add()方法不会对已遍历过的元素产生影响则未判断
// 源码 private class ListItr extends Itr implements ListIterator{ // 构造器,根据指定下标放置游标 ListItr(int index) { cursor = index; } // 游标之前是否还有元素,当前游标位置为0则表示之前没有元素 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(); } } // 获取游标后一个元素下标,即 cursor public int nextIndex() { return cursor; } // 获取游标前一个元素下标,即 cursor - 1 public int previousIndex() { return cursor-1; } // 修改元素方法,同原迭代器的remove不同,此方法未改变lastRet变量,因此可以连续修改元素,只是修改的为同一个元素 // 本方法虽然进行了修改次数同步,到实际有没有增加此处,与集合实现类的set()方法有关 public void set(E e) { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { AbstractList.this.set(lastRet, e); expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } // 本方法与原迭代器的remove一样,操作后变更了lastRet为-1,因此不能连续进行add操作, // 本方法同步了修改次数,不会直接导致ConcurrentModificationException异常 public void add(E e) { // 校验是否在生成迭代器后有进行集合的修改操作(一般为add操作) checkForComodification(); try { int i = cursor; AbstractList.this.add(i, e); lastRet = -1; cursor = i + 1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } }
// 错误示例
@Test
void contextLoads() {
List list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
ListIterator iterator = list.listIterator();
iterator.next();
iterator.remove();
iterator.add("11");
// 连续调用了remove()和set()方法,虽然中间存在一个add()方法,但未调用next(),抛出了java.lang.IllegalStateException异常
iterator.set("0");
}
03.提供给截取元素的集合(未实现RandomAccess) SubList
注意:
1. 本集合内部类继承AbstractList,因此本类的实现类调用subList()返回的集合对象不能转换为具体实现类的类型
2. 本方法中途也不允许对集合使用非本类的add()、remove()方法,不建议使用非本类的set()方法
3. 本方法重写了get()方法,虽然看似是截取了原集合,但其实只是修改了获取方法,虽然本方法内的add()、remove()、set()、get()等方法的下标针对的是截取后的集合,但是对象的指向还是原集合对象,因此增删改也会影响到原集合对象
4. 本方法的修改、移除等操作底层实际调用的是AbstractList的方法,本类的方法仅仅是进行了一些其他的处理,具体实现可以查看AbstractList对应方法详细解析
// 源码 class SubList04.提供给截取元素的集合(实现RandomAccess) RandomAccessSubListextends AbstractList { // 储存传入需截取的集合 private final AbstractList l; // 储存起始截取下标 private final int offset; // 储存需截取的元素数量(终止-起始) private int size; // 构造器,需传入需截取的集合对象,截取起始位置,截取终止位置 SubList(AbstractList list, int fromIndex, int toIndex) { // 校验下标,起始不能小于0,终止不能大于集合长度,起始不能大于终止 if (fromIndex < 0) throw new IndexOutOfBoundsException("fromIndex = " + fromIndex); if (toIndex > list.size()) throw new IndexOutOfBoundsException("toIndex = " + toIndex); if (fromIndex > toIndex) throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")"); l = list; offset = fromIndex; size = toIndex - fromIndex; this.modCount = l.modCount; } // 设置截取后的集合指定下标的元素 public E set(int index, E element) { rangeCheck(index); checkForComodification(); // 此处下标需增加起始下标位置,因为集合未去除之前和之后的元素 return l.set(index+offset, element); } // 获取截取后的集合指定下标的元素 public E get(int index) { rangeCheck(index); checkForComodification(); // 此处下标需增加起始下标位置,因为集合未去除之前和之后的元素 return l.get(index+offset); } // 获取截取后集合的元素个数 public int size() { checkForComodification(); return size; } // 给截取后的集合指定下标新增元素 public void add(int index, E element) { rangeCheckForAdd(index); checkForComodification(); // 此处下标需增加起始下标位置,因为集合未去除之前和之后的元素 l.add(index+offset, element); this.modCount = l.modCount; // 增加截取后集合的元素个数 size++; } // 移除截取后的集合指定下标的元素 public E remove(int index) { rangeCheck(index); checkForComodification(); E result = l.remove(index+offset); this.modCount = l.modCount; size--; return result; } // 移除截取后的集合指定范围的元素 protected void removeRange(int fromIndex, int toIndex) { checkForComodification(); l.removeRange(fromIndex+offset, toIndex+offset); this.modCount = l.modCount; // 减少截取后集合的元素个数 size -= (toIndex-fromIndex); } // 给截取后的集合新增指定集合内的全部元素 // 本方法调用的addAll(int index, Collection extends E> c)方法 public boolean addAll(Collection extends E> c) { return addAll(size, c); } // 给截取后的集合以指定下标为起始,新增指定集合内的全部元素 public boolean addAll(int index, Collection extends E> c) { rangeCheckForAdd(index); int cSize = c.size(); if (cSize==0) return false; checkForComodification(); l.addAll(offset+index, c); this.modCount = l.modCount; size += cSize; return true; } // 给截取后的集合生成List迭代器,实际生成的是AbstractList的List迭代器 public Iterator iterator() { return listIterator(); } // 给截取后的集合生成以指定下标前为游标起始位置的List迭代器,实际生成的是AbstractList的List迭代器 public ListIterator listIterator(final int index) { checkForComodification(); rangeCheckForAdd(index); return new ListIterator () { // 此处下标需增加起始下标位置,因为集合未去除之前和之后的元素 private final ListIterator i = l.listIterator(index+offset); // 游标后是否有元素 public boolean hasNext() { return nextIndex() < size; } // 获取游标后的元素,若游标后没有元素抛出NoSuchElementException异常 public E next() { if (hasNext()) return i.next(); else throw new NoSuchElementException(); } // 游标前是否有元素 public boolean hasPrevious() { return previousIndex() >= 0; } // 获取游标前的元素,若游标前没有元素抛出NoSuchElementException异常 public E previous() { if (hasPrevious()) return i.previous(); else throw new NoSuchElementException(); } // 获取游标后一个元素的下标,需要减去起始元素下标,因为集合未去除之前和之后的元素 public int nextIndex() { return i.nextIndex() - offset; } // 获取游标前一个元素的下标,需要减去起始元素下标,因为集合未去除之前和之后的元素 public int previousIndex() { return i.previousIndex() - offset; } // 移除元素,同步了修改次数,调用的方法有校验lastRet,不能连续调用 public void remove() { i.remove(); SubList.this.modCount = l.modCount; // 减少截取后集合的元素个数 size--; } // 修改元素 public void set(E e) { i.set(e); } // 新增元素,同步了修改次数,调用的方法有校验lastRet,不能连续调用 public void add(E e) { i.add(e); SubList.this.modCount = l.modCount; // 增加截取后集合的元素个数 size++; } }; } // 截取当前集合指定范围的元素 public List subList(int fromIndex, int toIndex) { return new SubList<>(this, fromIndex, toIndex); } // 校验集合下标,下标可以等于集合个数,用于移除、修改元素,最后一位下标是size-1,size位置没有元素 private void rangeCheck(int index) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } // 校验集合下标,下标不能等于集合个数,用于新增相关校验,最后一位下标是size-1,可以在size位置新增元素 private void rangeCheckForAdd(int index) { if (index < 0 || index > size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } // 校验集合下标异常信息 private String outOfBoundsMsg(int index) { return "Index: "+index+", Size: "+size; } // 校验迭代器生成后集合是否提供非迭代器方法修改元素(一般为新增和移除) private void checkForComodification() { if (this.modCount != l.modCount) throw new ConcurrentModificationException(); } }
注意:
1. 本类继承了SubList,实际上还是调用了未实现RandomAccess接口时调用的内部类生成集合
2. 本类为List的底层类,未进行RandomAccess实现的特别区分,仅留下了框架,实现类若需不同处理需要重写此内部类
拓展:RandomAccess是一个标志接口,对于一些方法可以通过此标识判断其是否支持快速随机访问
// 源码 class RandomAccessSubList保护方法 01.移除指定下标范围的元素 removeRange(int fromIndex, int toIndex)extends SubList implements RandomAccess { // 继承了SubList,调用的是SubList的构造器 RandomAccessSubList(AbstractList list, int fromIndex, int toIndex) { super(list, fromIndex, toIndex); } // 继承了SubList,调用的是SubList的构造器 public List subList(int fromIndex, int toIndex) { return new RandomAccessSubList<>(this, fromIndex, toIndex); } }
参数:
1. fromIndex —— 起始下标
2. toIndex —— 终止下标
// 源码
protected void removeRange(int fromIndex, int toIndex) {
// 创建一个游标开始位置为指定开始下标之前的迭代器
ListIterator it = listIterator(fromIndex);
// 循环开始下标至结束下标,一个一个元素移除
for (int i=0, n=toIndex-fromIndex; i
it.next();
it.remove();
}
}
抽象方法
01.获取指定下标的元素 get(int index)
// 源码 abstract public E get(int index);私有方法 01.校验下标是否超过集合下标范围 rangeCheckForAdd(int index)
参数:index —— 需要校验的下标
备注:超过集合下标范围有两种情况 —— 小于0和大于集合长度
// 源码
private void rangeCheckForAdd(int index) {
if (index < 0 || index > size())
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
02.超出下标异常信息 outOfBoundsMsg(int index)
参数:index —— 校验出现异常信息的只当下标
备注:信息格式 —— Index: 指定下标, Size: 集合长度
// 源码
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size();
}
// 示例
@Test
void contextLoads() {
List list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
// Index: 7, Size: 5
list.add(7,"10");
}
公有方法
01.新增元素 add(E e)
参数:e —— 增加的元素
注意:
1. 此新增元素方法调用的是add(int index, E element),指定的下标为集合长度(下标从0开始,长度从1开始),长度即最后一个元素后一位的下标
2. 本方法调用的add(int index, E element)是必须重写,未重写会抛出UnsupportedOperationException异常
// 源码
public boolean add(E e) {
add(size(), e);
return true;
}
02.新增元素到指定下标 add(int index, E element)
参数:
1. index —— 新增元素的指定下标
2. element —— 新增的元素
注意:本方法必须重写,未重写会抛出UnsupportedOperationException异常
// 源码
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
03.设置指定下标的元素为指定元素 set(int index, E element)
参数:
1. index —— 修改的元素下标
2. element —— 修改为的元素
注意:本方法必须重写,未重写会抛出UnsupportedOperationException异常
// 源码
public E set(int index, E element) {
throw new UnsupportedOperationException();
}
04.移除指定下标的元素 remove(int index)
参数: index —— 移除元素的下标
注意:本方法必须重写,未重写会抛出UnsupportedOperationException异常
// 源码
public E remove(int index) {
throw new UnsupportedOperationException();
}
05.获取指定元素第一次出现下标 indexOf(Object o)
参数: o —— 需获取的元素
注意:
1. 获取的是集合中指定元素第一次出现的下标
2. 返回-1表示没有找到指定元素
补充:previousIndex() —— 返回游标前一个元素的下标,游标开始时在第一个元素前,调用next()后游标后移
// 源码
public int indexOf(Object o) {
ListIterator it = listIterator();
if (o==null) {
// 如果指定元素为null需要单独比较,直接使用equals会空指针
while (it.hasNext())
if (it.next()==null)
return it.previousIndex();
} else {
while (it.hasNext())
// 此处已调用了next(),游标已后移,因此返回的下标为游标的前一个元素下标
if (o.equals(it.next()))
return it.previousIndex();
}
return -1;
}
06.获取指定元素最后一次出现的下标 lastIndexOf(Object o)
参数: o —— 需获取的元素
补充:
1. listIterator(size()) —— 创建一个开始游标在最后一个元素之后的迭代器
2. it.hasPrevious() —— 判断当前游标前面有没有元素
3. it.nextIndex() —— 获取当前游标后一位的元素下标 (此处是逆序的迭代器,因此要返回游标后的下标),调用it.previous()后游标前移
4. 以上这些方法在本类中有实现,后续有说明
5. 该方法获取的是最后一次出现下标,因此创建迭代器的游标在最后,反向遍历
6. 该方法未找到指定元素返回-1
// 源码
public int lastIndexOf(Object o) {
ListIterator it = listIterator(size());
if (o==null) {
// 如果指定元素为null需要单独比较,直接使用equals会空指针
while (it.hasPrevious())
if (it.previous()==null)
return it.nextIndex();
} else {
while (it.hasPrevious())
// 此处已调用了previous(),游标已后移,因此返回的下标为游标的前一个元素下标
if (o.equals(it.previous()))
return it.nextIndex();
}
return -1;
}
07.清空集合 clear()
注意:
1. 本方法调用移除指定下标范围的元素,开始下标为0,结束下标为集合元素个数
2. 本方法实际上是一个一个元素移除的
// 源码
public void clear() {
removeRange(0, size());
}
08.在指定下标新增指定集合内全部元素 addAll(int index, Collection extends E> c)
参数:
1. index —— 新增集合元素的起始下标
2. c —— 新增的集合
注意:
1. 此方法实际调用的是add(int index, E element),此方法必须重写,否则会抛出UnsupportedOperationException异常
2. 本方法是循环全部指定集合进行多次元素插入实现的,因此重复元素也会插入,不会去重
// 源码
public boolean addAll(int index, Collection extends E> c) {
rangeCheckForAdd(index);
boolean modified = false;
for (E e : c) {
add(index++, e);
modified = true;
}
return modified;
}
09.生成当前集合的迭代器 iterator()
注意:此处生成的迭代器为本类中的迭代器内部类
// 源码 public Iterator10.生成当前集合的List迭代器 listIterator()iterator() { return new Itr(); }
注意:
1. 此处生成的迭代器为本类中的List迭代器内部类
2. 指定游标位置设置在集合开头便于遍历全部元素
// 源码 public ListIterator11.生成当前集合指定下标前为起始位的List迭代器 listIterator(final int index)listIterator() { return listIterator(0); }
注意:此处生成的迭代器为本类中的List迭代器内部类
// 源码 public ListIterator12.截取集合指定范围内的元素 subList(int fromIndex, int toIndex)listIterator(final int index) { // 校验指定下标是否小于0或超过集合长度 rangeCheckForAdd(index); return new ListItr(index); }
参数:
1. fromIndex —— 起始下标
2. toIndex —— 终止下标
注意:本方法返回的集合虽然看似是截取了原集合的一部分,但其实只是修改了get()等方法,返回的对象仍然是原集合对象,因此对返回的集合进行修改仍会变更原集合
补充:
1. RandomAccess是一个标志型的接口,内部无内容也无注释
2. 根据本类是否实现了RandomAccess接口匹配不同的处理,主要用于不同的实现类使用效率更高的处理方法
3. 本类为List的底层类,未进行RandomAccess实现的特别区分,因此本类无论是否实现了RandomAccess,均返回的是SubList内部类,但此类的实现类ArrayList实现了此接口,而LinkedList则没有实现此接口
// 源码 public ListsubList(int fromIndex, int toIndex) { return (this instanceof RandomAccess ? new RandomAccessSubList<>(this, fromIndex, toIndex) : new SubList<>(this, fromIndex, toIndex)); }
// 示例
@Test
void contextLoads5() {
List list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
List list1 = list.subList(1, 3);
// [2, 3]
System.out.println("截取后的集合" + list1);
// [1, 2, 3, 4, 5]
System.out.println("原集合:" + list.toString());
list1.set(0,"11");
// [11, 3]
System.out.println("截取后的集合" + list1);
// [1, 11, 3, 4, 5]
System.out.println("原集合:" + list.toString());
list1.add("22");
// [11, 3, 22]
System.out.println("截取后的集合" + list1);
// [1, 11, 3, 22, 4, 5]
System.out.println("原集合:" + list.toString());
list1.remove(2);
// [11, 3]
System.out.println("截取后的集合" + list1);
// [1, 11, 3, 4, 5]
System.out.println("原集合:" + list.toString());
}
13.比较方法 equals(Object o)
参数:o —— 比较的对象
// 源码
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof List))
return false;
ListIterator 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());
}
14.哈希值 hashCode
计算方法:31 * 当前哈希值 + 元素哈希值(null为0),当前哈希值随着循环不断累积,首位为1
// 源码
public int hashCode() {
int hashCode = 1;
for (E e : this)
hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
return hashCode;
}



