热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

java迭代面试_奇葩java迭代器面试题,还真有很多人踩坑

有位小朋友最近正在为年后换工作做准备,但是遇到一个问题,觉得很不可思议的一道笔试题。然后我把这道题发到技术群里,发现很多人居然不知道&#x

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

奇怪的笔试题

阅读下面这段代码,请写出这段代码的输出内容:

import java.util.ArrayList;

import java.util.Iterator;

import java.util.*;

public class Test {

public static void main(String[] args) {

List list &#61; new ArrayList<>();

list.add("1");

list.add("2");

list.add("3");

Iterator iterator &#61; list.iterator();

while (iterator.hasNext()) {

String str &#61; (String) iterator.next();

if (str.equals("2")) {

iterator.remove();

}

}

while (iterator.hasNext()) {

System.out.println(iterator.next());

}

System.out.println("4");

}

}

他写出来的答案是&#xff1a;

1

3

4

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

c7629f4d13c149a4af794971baefcd08.png

~

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

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

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

这里的Iterator是什么&#xff1f;

迭代器是一种模式、详细可见其设计模式&#xff0c;可以使得序列类型的数据结构的遍历行为与被遍历的对象分离&#xff0c;即我们无需关心该序列的底层结构是什么样子的。只要拿到这个对象,使用迭代器就可以遍历这个对象的内部

Iterable 实现这个接口的集合对象支持迭代&#xff0c;是可以迭代的。实现了这个可以配合foreach使用~

Iterator 迭代器&#xff0c;提供迭代机制的对象&#xff0c;具体如何迭代是这个Iterator接口规范的。

Iterator说明

public interface Iterator {

//每次next之前&#xff0c;先调用此方法探测是否迭代到终点

boolean hasNext();

//返回当前迭代元素 &#xff0c;同时&#xff0c;迭代游标后移

E next();

/*删除最近一次已近迭代出出去的那个元素。

只有当next执行完后&#xff0c;才能调用remove函数。

比如你要删除第一个元素&#xff0c;不能直接调用 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 &#61; -1; // index of last element returned; -1 if no such

//modCountshi ArrayList中的属性&#xff0c;当添加或删除的时候moCount值会增加或者减少

//这里主要是给fail-fast使用&#xff0c;避免一遍在遍历&#xff0c;一遍正在修改导致数据出错

//此列表在结构上被修改的次数。结构修改是指改变结构尺寸的修改列表&#xff0c;

//或者以这样的方式对其进行扰动,进步可能会产生错误的结果。

int expectedModCount &#61; modCount;

public boolean hasNext() {

//cursor初始值为0&#xff0c;没掉一次next方法就&#43;1

//size是ArrayList的大小

return cursor !&#61; size;

}

&#64;SuppressWarnings("unchecked")

public E next() {

checkForComodification();

int i &#61; cursor;

if (i >&#61; size)

throw new NoSuchElementException();

//把ArrayList中的数组赋给elementData

Object[] elementData &#61; ArrayList.this.elementData;

if (i >&#61; elementData.length)

throw new ConcurrentModificationException();

//每调用一次next方法&#xff0c;游标就加1

//cursor&#61;lastRet&#43;1

cursor &#61; i &#43; 1;

//返回ArrayList中的元素

return (E) elementData[lastRet &#61; i];

}

public void remove() {

if (lastRet <0)

throw new IllegalStateException();

checkForComodification();

try {

//调用ArrayList中remove方法&#xff0c;溢出该元素

ArrayList.this.remove(lastRet);

//cursor&#61;lastRet&#43;1&#xff0c;

//所以此时相当于cursor&#61;cursor-1

cursor &#61; lastRet;

lastRet &#61; -1;

expectedModCount &#61; modCount;

} catch (IndexOutOfBoundsException ex) {

throw new ConcurrentModificationException();

}

}

final void checkForComodification() {

if (modCount !&#61; expectedModCount)

throw new ConcurrentModificationException();

}

}

再回到上面题目中&#xff1a;

第一个iterator.hasNext()

第1次循环

hasNext方法中&#xff1a;cursor0&#xff0c; size3&#xff0c;所以cursor !&#61; size返回true。

next方法中&#xff1a;cursor&#61;0&#43;1。返回"1"。

第2次循环

hasNext方法中&#xff1a;cursor1&#xff0c; size3&#xff0c;所以cursor !&#61; size返回true。

next方法中&#xff1a;cursor&#61;1&#43;1。返回"2"。

remove方法中&#xff1a;cursorcursor-12-1&#61;1&#xff0c;把ArrayList中的"2"给删除了&#xff0c;所以size&#61;&#61;2。

第3次循环

hasNext方法中&#xff1a;cursor1&#xff0c; size2&#xff0c;那么cursor !&#61; size返回true。

next方法中&#xff1a;cursor&#61;1&#43;1&#61;&#61;2&#xff1b;返回"3"。

第4次循环

hasNext方法中&#xff1a;cursor2&#xff0c; size2&#xff0c;那么cursor !&#61; size返回false。

第二个iterator.hasNext()

hasNext方法中&#xff1a;cursor2&#xff0c; size2&#xff0c;所以cursor !&#61; size返回false。

所以&#xff0c;最后只输出"4"&#xff0c;即答案为4.

Iterator与泛型搭配

Iterator对集合类中的任何一个实现类&#xff0c;都可以返回这样一个Iterator对象。可以适用于任何一个类。

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

foreach和Iterator的关系

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

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

采用ArrayList对随机访问比较快&#xff0c;而for循环中的get()方法&#xff0c;采用的即是随机访问的方法&#xff0c;因此在ArrayList里&#xff0c;for循环较快

采用LinkedList则是顺序访问比较快&#xff0c;iterator中的next()方法&#xff0c;采用的即是顺序访问的方法&#xff0c;因此在LinkedList里&#xff0c;使用iterator较快

从数据结构角度分析,for循环适合访问顺序结构,可以根据下标快速获取指定元素.而Iterator 适合访问链式结构,因为迭代器是通过next()和Pre()来定位的.可以访问没有顺序的集合.

而使用 Iterator 的好处在于可以使用相同方式去遍历集合中元素&#xff0c;而不用考虑集合类的内部实现(只要它实现了 java.lang.Iterable 接口)&#xff0c;如果使用 Iterator 来遍历集合中元素&#xff0c;一旦不再使用 List 转而使用 Set 来组织数据&#xff0c;那遍历元素的代码不用做任何修改&#xff0c;如果使用 for 来遍历&#xff0c;那所有遍历此集合的算法都得做相应调整,因为List有序,Set无序,结构不同,他们的访问算法也不一样.(还是说明了一点遍历和集合本身分离了)。

总结

迭代出来的元素都是原来集合元素的拷贝。

Java集合中保存的元素实质是对象的引用&#xff0c;而非对象本身。

迭代出的对象也是引用的拷贝&#xff0c;结果还是引用。那么如果集合中保存的元素是可变类型的&#xff0c;那么可以通过迭代出的元素修改原集合中的对象。



推荐阅读
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • 本文介绍了一个Java猜拳小游戏的代码,通过使用Scanner类获取用户输入的拳的数字,并随机生成计算机的拳,然后判断胜负。该游戏可以选择剪刀、石头、布三种拳,通过比较两者的拳来决定胜负。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
author-avatar
大侠aaaaaaaaaaa_225
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有