阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

迭代器笔试题,看看你会不会?

62次阅读
没有评论

共计 3791 个字符,预计需要花费 10 分钟才能阅读完成。

导读 有位小朋友最近正在为年后换工作做准备,但是遇到一个问题,觉得很不可思议的一道笔试题。然后我把这道题发到技术群里,发现很多人居然不知道,很多都是连蒙带猜的说。感觉很有必要写一篇文章来说道说道。

有位小朋友最近正在为年后换工作做准备,但是遇到一个问题,觉得很不可思议的一道笔试题。然后我把这道题发到技术群里,发现很多人居然不知道,很多都是连蒙带猜的说。感觉很有必要写一篇文章来说道说道。
迭代器笔试题,看看你会不会?

迭代器笔试题,看看你会不会?
奇怪的笔试题阅读下面这段代码,请写出这段代码的输出内容:

import java.util.ArrayList; 
import java.util.Iterator; 
import java.util.*; 
 
public class Test {public static void main(String[] args) {List list = new ArrayList(); 
        list.add("1"); 
        list.add("2"); 
        list.add("3"); 
        Iterator iterator = list.iterator(); 
        while (iterator.hasNext()) {String str = (String) iterator.next(); 
            if (str.equals("2")) {iterator.remove(); 
            } 
        } 
        while (iterator.hasNext()) {System.out.println(iterator.next()); 
        } 
        System.out.println("4"); 
    } 
} 

他写出来的答案是:

1 
3 
4

奇怪的是,你把这道题目发给你身边人,让他们回答这道面试题输出结果是什么,说这个结果的人非常多。不行你试试。

答案明显不对,因为在第一个 while 里的 iterator.hasNext()==false 后才会到第二个 while 里来,同一个 Iterator 对象,前面调一次 iterator.hasNext()==false,再判断一次结果不还是一样吗?,

所以第二个 while 判断为 false,也就不会再去遍历 iterator 了,由此可知本体答案是:4。

下面我们来分析一下为什么是具体底层是怎么实现的。

这里的 Iterator 是什么?

迭代器是一种模式、详细可见其设计模式,可以使得序列类型的数据结构的遍历行为与被遍历的对象分离,即我们无需关心该序列的底层结构是什么样子的。只要拿到这个对象, 使用迭代器就可以遍历这个对象的内部
Iterable 实现这个接口的集合对象支持迭代,是可以迭代的。实现了这个可以配合 foreach 使用~
Iterator 迭代器,提供迭代机制的对象,具体如何迭代是这个 Iterator 接口规范的。
Iterator 说明

public interface Iterator {  
    // 每次 next 之前,先调用此方法探测是否迭代到终点 
    boolean hasNext(); 
    // 返回当前迭代元素,同时,迭代游标后移 
    E next();  
    /* 删除最近一次已近迭代出出去的那个元素。只有当 next 执行完后,才能调用 remove 函数。比如你要删除第一个元素,不能直接调用 remove()   而要先 next 一下(); 
     在没有先调用 next 就调用 remove 方法是会抛出异常的。这个和 MySQL 中的 ResultSet 很类似 
    */ 
    default void remove() {throw new UnsupportedOperationException("remove"); 
    }  
    default void forEachRemaining(Consumer super E> action) {Objects.requireNonNull(action); 
        while (hasNext()) 
            action.accept(next()); 
    } 
} 

这里的实现类是 ArrayList 的内部类 Itr。

private class Itr implements Iterator { 
        int cursor;       // index of next element to return 
        int lastRet = -1; // index of last element returned; -1 if no such 
        //modCountshi ArrayList 中的属性,当添加或删除的时候 moCount 值会增加或者减少 
        // 这里主要是给 fail-fast 使用,避免一遍在遍历,一遍正在修改导致数据出错 
        // 此列表在结构上被修改的次数。结构修改是指改变结构尺寸的修改列表,// 或者以这样的方式对其进行扰动, 进步可能会产生错误的结果。int expectedModCount = modCount; 
         
        public boolean hasNext() { 
            //cursor 初始值为 0,没掉一次 next 方法就 +1 
            //size 是 ArrayList 的大小 
            return cursor != size; 
        } 
 
        @SuppressWarnings("unchecked") 
        public E next() {checkForComodification(); 
            int i = cursor; 
            if (i >= size) 
                throw new NoSuchElementException(); 
            // 把 ArrayList 中的数组赋给 elementData 
            Object[] elementData = ArrayList.this.elementData; 
            if (i >= elementData.length) 
                throw new ConcurrentModificationException(); 
            // 每调用一次 next 方法,游标就加 1 
            //cursor=lastRet+1 
            cursor = i + 1; 
            // 返回 ArrayList 中的元素 
            return (E) elementData[lastRet = i]; 
        } 
 
        public void remove() {if (lastRet 
再回到上面题目中:

第一个 iterator.hasNext()

第 1 次循环

hasNext 方法中:cursor==0,size==3,所以 cursor != size 返回 true。
next 方法中:cursor=0+1。返回 ”1″。

第 2 次循环

hasNext 方法中:cursor==1,size==3,所以 cursor != size 返回 true。
next 方法中:cursor=1+1。返回 ”2″。
remove 方法中:cursor==cursor-1==2-1=1,把 ArrayList 中的 ”2″ 给删除了,所以 size==2。

第 3 次循环

hasNext 方法中:cursor==1,size==2,那么 cursor != size 返回 true。
next 方法中:cursor=1+1==2; 返回 ”3″。

第 4 次循环

hasNext 方法中:cursor==2,size==2,那么 cursor != size 返回 false。
第二个 iterator.hasNext()

hasNext 方法中:cursor==2,size==2,所以 cursor != size 返回 false。

所以,最后只输出 ”4″,即答案为 4.

Iterator 与泛型搭配

Iterator 对集合类中的任何一个实现类,都可以返回这样一个 Iterator 对象。可以适用于任何一个类。
因为集合类 (List 和 Set 等) 可以装入的对象的类型是不确定的, 从集合中取出时都是 Object 类型, 用时都需要进行强制转化, 这样会很麻烦, 用上泛型, 就是提前告诉集合确定要装入集合的类型, 这样就可以直接使用而不用显示类型转换. 非常方便.

foreach 和 Iterator 的关系

for each 以用来处理集合中的每个元素而不用考虑集合定下标。就是为了让用 Iterator 简单。但是删除的时候,区别就是在 remove,循环中调用集合 remove 会导致原集合变化导致错误,而应该用迭代器的 remove 方法。

使用 for 循环还是迭代器 Iterator 对比

采用 ArrayList 对随机访问比较快,而 for 循环中的 get()方法,采用的即是随机访问的方法,因此在 ArrayList 里,for 循环较快
采用 LinkedList 则是顺序访问比较快,iterator 中的 next()方法,采用的即是顺序访问的方法,因此在 LinkedList 里,使用 iterator 较快
从数据结构角度分析,for 循环适合访问顺序结构, 可以根据下标快速获取指定元素. 而 Iterator 适合访问链式结构, 因为迭代器是通过 next()和 Pre()来定位的. 可以访问没有顺序的集合.
而使用 Iterator 的好处在于可以使用相同方式去遍历集合中元素,而不用考虑集合类的内部实现(只要它实现了 java.lang.Iterable 接口),如果使用 Iterator 来遍历集合中元素,一旦不再使用 List 转而使用 Set 来组织数据,那遍历元素的代码不用做任何修改,如果使用 for 来遍历,那所有遍历此集合的算法都得做相应调整, 因为 List 有序,Set 无序, 结构不同, 他们的访问算法也不一样.(还是说明了一点遍历和集合本身分离了)。

总结

迭代出来的元素都是原来集合元素的拷贝。
Java 集合中保存的元素实质是对象的引用,而非对象本身。
迭代出的对象也是引用的拷贝,结果还是引用。那么如果集合中保存的元素是可变类型的,那么可以通过迭代出的元素修改原集合中的对象。

阿里云 2 核 2G 服务器 3M 带宽 61 元 1 年,有高配

腾讯云新客低至 82 元 / 年,老客户 99 元 / 年

代金券:在阿里云专用满减优惠券

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-07-25发表,共计3791字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中