目录
一、强制类型转换的问题
需要注意:ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException异常,即java.util.RandomAccessSubList cannot be cast to java.util.ArrayList.
首先我们先介绍 subList的作用:获取 ArrayList 的子列表
public class Test01 {
public static void main(String[] args) {
ArrayList<Integer> list=new ArrayList<>();
for(int i=0;i<10;i++){
list.add(i);
}
List<Integer> ret = list.subList(0, 5);
System.out.println("ArrayList:"+list);
System.out.println("SubList:"+ret);
}
}
这段代码主要演示了ArrayList
的使用以及通过subList
方法获取子列表后,对原列表进行修改所产生的影响。具体来说,它首先创建了一个包含整数 0
到 9
的ArrayList
,然后从中获取了一个子列表(包含索引 0
到 4
的元素),接着输出了原列表和子列表的内容。
如果我们此时将subList的结果进行强制类型转换:
public class Test02 {
public static void main(String[] args) {
ArrayList<Integer> list=new ArrayList<>();
for(int i=0;i<10;i++){
list.add(i);
}
ArrayList<Integer> ret =(ArrayList<Integer>) list.subList(0, 5);
System.out.println("ArrayList:"+list);
System.out.println("SubList:"+ret);
}
}
此时即可验证:ArrayList的subList结果不可强转成ArrayList。
具体的原因我们可以查看内部源码:
然后我们查看 AbstractList 的源码:
正确的使用方式:借助多态的特性
总结:subList 返回的是 ArrayList 的内部类 SubList,并不是 ArrayList ,而是 ArrayList 的一个视图,对于SubList子列表的所有操作最终会反映到原列表上。
二、subList的应用场景的问题
在subList场景中,我们要高度注意对原集合元素个数的修改,会导致子列表的遍历、增加、删除均会产生ConcurrentModificationException 异常。
上述问题的产生我们通过下面的案例演示:
public class Test03 {
public static void main(String[] args) {
ArrayList<Integer> list=new ArrayList<>();
for(int i=0;i<10;i++){
list.add(i);
}
List<Integer> ret = list.subList(0, 5);
System.out.println("ArrayList:"+list);
System.out.println("SubList:"+ret);
list.remove(0);
System.out.println("SubList:"+ret);
}
}
当我们对原ArrayList进行修改操作时,由于subList返回的是ArrayList的一个视图,ArrayList的修改会影响到subList。
针对上述出现的问题,提供两种解决方案。
2.1 解决方案:克隆子列表
public class Test04 {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i);
}
List<Integer> ret = new ArrayList<>(list.subList(0, 5));
System.out.println("ArrayList:" + list);
System.out.println("SubList:" + ret);
list.remove(0);
System.out.println("SubList:" + ret);
}
}
创建子列表的一个副本,这样原列表的修改就不会影响到子列表。可以使用ArrayList
的构造函数来创建一个新的列表,将子列表的元素复制到新列表中。通过new ArrayList<>(list.subList(0, 5))
创建了一个新的ArrayList
,它包含了原子列表的元素副本。这样,当修改原列表list
时,新的子列表ret
不会受到影响,不会抛出ConcurrentModificationException
异常。
2.2 解决方案:使用迭代器进行修改
public class Test05 {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i);
}
List<Integer> ret = list.subList(0, 5);
System.out.println("ArrayList:" + list);
System.out.println("SubList:" + ret);
ListIterator<Integer> listIterator = ret.listIterator();
// 条件判断:检查迭代器是否有下一个元素
if (listIterator.hasNext()) {
// 如果有下一个元素,移动到下一个元素并删除它。
listIterator.next();
listIterator.remove();
}
System.out.println("SubList:" + ret);
}
}
- listIterator.next():将迭代器移动到列表中的下一个元素(ret 的第一个元素),并返回该元素。
- listIterator.remove():删除迭代器当前指向的元素。调用 remove() 方法之前,必须先调用 next() 或 previous() 方法来定位到要删除的元素。