【软件构造】集合遍历时删除元素的问题和迭代器

集合的遍历方式有三种,分别对应着三种删除元素的情况:

1、利用for循环

        List<String> list=new ArrayList<>();
        list.add("1111");
        list.add("2222");
        list.add("3333");
        for (int i=0;i<list.size();i++){
            if (list.get(i).length()==4){
                list.remove(i);
            }
        }
        for (String i:list) {
            System.out.println(i);
        }

上面的执行结果是2222,这也是这种方式存在的问题,这种遍历方式只支持删除一个元素,如果删除多个元素,就会出错。因为在删除某个元素后,该元素后面的元素会自己向前移动,这时当前索引i已经指向了下一个元素,但是在下一次循环中,索引i又执行了加一操作,所以有一个元素被“跨过”了。比如说删除了第一个元素,后面的元素前移,当前索引已经指向了第二个元素,所以下一次循环中,访问到的其实是第三个元素,第二个元素就没有被访问到。所以要想正常使用,需要在if语句最后加上break,保证可以正确删除一个元素后退出循环。


2、利用foreach

        List<String> list=new ArrayList<>();
        list.add("1111");
        list.add("2222");
        list.add("3333");
        for(String i:list){
            if(i.length()==4){
                list.remove(i);
                break;
            }
        }
        for (String i:list) {
            System.out.println(i);
        }

上面的代码的执行结果是2222,3333。可以看到这一条和第一条是很像的,但是在if语句中有一个break语句,如果没有break语句,在运行时,就会抛出ConcurrentModificationException异常,所以这种方式只能用于删除一个元素。

那么为什么会抛出这种异常呢?去掉break,查看反编译代码,发现foreach的实现其实是用到了迭代器,如下

        List<String> list = new ArrayList();
        list.add("1111");
        list.add("2222");
        list.add("3333");
        Iterator var2 = list.iterator();

        String i;
        while(var2.hasNext()) {
            i = (String)var2.next();
            if (i.length() == 4) {
                list.remove(i);
            }
        }

要想了解为什么会抛出这个异常,我们需要先了解迭代器的工作原理:迭代器其实就是集合的一个“快照”,他保存的是集合的结构,但是要求迭代器必须是最新的,也就是说,在获取迭代器后,如果集合的结构发生了改变,呢么这个迭代器就作废了,要想继续使用,只能重新获取一个构造器,否则就会抛出上面的异常。介绍了迭代器的工作原理,也就引出了第三种遍历方式:利用迭代器遍历


3、利用迭代器遍历

        List<String> list=new ArrayList<>();

        list.add("1111");
        list.add("2222");
        list.add("3333");
        Iterator<String> it = list.iterator();
        while (it.hasNext()){
            if(it.next().length()==4){
                it.remove();
            }
        }
        for (String i:list) {
            System.out.println(i);
        }

上面代码的执行结果是什么也没输出,正确!

上面说到,如果集合的结构发生了改变,与迭代器结构不同,就需要重新获取迭代器。集合结构与迭代器不同,是因为使用了集合的remove方法,该方法只会改变集合的结构,而不改变迭代器的结构。如果我们使用迭代器的remove方法,他会在迭代器中和集合中同时删除这个元素,所以两者的结构还是相同的,不会抛出异常,结果也不会出现错误。所以,如果要删除多个元素,就需要使用这种方式。

### 回答1: 我可以提供一些Java集合相关的面试题,包括:1.什么是集合?2.什么是集合框架?3.HashMap和Hashtable有什么区别?4.ArrayList和LinkedList有什么区别?5.什么是迭代器?6.什么是泛型?7.什么是线程安全的集合?8.什么是同步集合? ### 回答2: Java集合Java中常用的数据结构,提供了许多功能强大且灵活的类和接口,可以方便地处理各种数据操作。下面我将回答一些常见的和Java集合相关的面试题。 1. 请解释一下Java集合框架。 Java集合框架是一组用来存储和操作数据的类和接口的集合。它提供了各种集合类型(如List、Set、Map等),以及用于集合操作的方法和算法。Java集合框架的设计目标是提供高性能、可靠性和安全性的数据结构,同将最小的编程复杂性和性能开销传递给开发人员。 2. List和Set之间有什么区别? List是一个有序的集合,允许重复元素。可以通过索引访问集合中的元素。Set是一个不包含重复元素的集合,不保证元素的顺序。在List中,元素可以根据插入的顺序进行访问和遍历,而在Set中,无法根据插入的顺序访问元素。 3. HashMap和HashTable有什么区别? HashMap和Hashtable都是实现了Map接口的键值对集合。主要区别在于HashMap是非线程安全的,而Hashtable是线程安全的。此外,HashMap允许使用null键和null值,而Hashtable不允许。在性能上,HashMap要优于Hashtable,因为HashMap使用了更高效的哈希算法。 4. 请解释一下ArrayList和LinkedList之间的差异。 ArrayList和LinkedList都是List接口的实现类。ArrayList是基于动态数组实现的,因此在随机访问和获取元素性能较好。而LinkedList是基于双向链表实现的,因此在插入和删除元素性能较好。因此,如果需要频繁进行随机访问操作,则应选择ArrayList;如果需要频繁进行插入和删除操作,应选择LinkedList。 5. 请解释一下迭代器(Iterator)是什么? 迭代器是用于遍历集合中元素的对象。由于不同的集合类型内部的数据结构不同,使用通用的for循环进行遍历可能会有问题迭代器提供了一种统一的方式来遍历集合,无论集合内部的数据结构如何,都能够正确地获取元素。可以使用迭代器的方法来逐个访问和操作集合中的元素。 ### 回答3: 面试题一: ArrayList和LinkedList有什么区别? ArrayList和LinkedList都是Java集合框架中的List接口的实现类,它们的主要区别在于底层数据结构和性能。 ArrayList底层使用数组实现,它的读取效率很高,间复杂度为O(1),但是在插入和删除元素会导致数组的扩容和复制,间复杂度为O(n)。因此,适合对元素的访问操作较多,而对插入和删除操作较少的场景。此外,ArrayList还支持随机访问,可以通过索引来直接访问元素。 LinkedList底层使用双向链表实现,它的插入和删除操作效率很高,间复杂度为O(1),因为只需要改变前后节点的指针即可。但是在读取元素需要从头开始遍历链表,间复杂度为O(n)。因此,适合对插入和删除操作较多,而对元素的访问操作较少的场景。此外,LinkedList还支持快速的头部和尾部操作。 总结起来,ArrayList适用于元素访问频繁的场景,而LinkedList适用于插入和删除频繁的场景。 面试题二: HashSet和TreeSet有什么区别? HashSet和TreeSet都是Java集合框架中的Set接口的实现类,它们的主要区别在于元素的存储和排序方式。 HashSet底层使用哈希表实现,它不保证元素的顺序,存储和检索的速度都很快,间复杂度为O(1)。HashSet使用元素的hashCode()方法和equals()方法来判断元素的唯一性,因此要求元素必须实现hashCode()和equals()方法。 TreeSet底层使用红黑树实现,它可以对元素进行排序,默认按照元素的自然顺序进行排序,或者通过传入的Comparator对象来进行自定义排序。TreeSet的存储和检索速度较慢,间复杂度为O(logn)。TreeSet要求元素必须实现Comparable接口或者在构造TreeSet传入Comparator对象来进行比较。 总结起来,HashSet适用于需要快速存储和检索元素,并且不需要考虑元素的排序的场景。而TreeSet适用于需要对元素进行排序的场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值