关于 subList 你不知道的事情

目录

一、强制类型转换的问题

二、subList的应用场景的问题

2.1 解决方案:克隆子列表

2.2 解决方案:使用迭代器进行修改


一、强制类型转换的问题

需要注意: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() 方法来定位到要删除的元素。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

敖云岚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值