Java迭代器原理

1迭代器模式

迭代器是一种设计模式,这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。

一般实现方式如下:(来自)

 

public interface Iterator {public boolean hasNext();public Object next();
}
public interface Container {public Iterator getIterator();
}
public class NameRepository implements Container {public String names[] = {"Robert" , "John" ,"Julie" , "Lora"};@Overridepublic Iterator getIterator() {return new NameIterator();}private class NameIterator implements Iterator {int index;@Overridepublic boolean hasNext() {if(index < names.length){return true;}return false;}@Overridepublic Object next() {if(this.hasNext()){return names[index++];}return null;}        }
}
public class IteratorPatternDemo {public static void main(String[] args) {NameRepository namesRepository = new NameRepository();for(Iterator iter = namesRepository.getIterator(); iter.hasNext();){String name = (String)iter.next();System.out.println("Name : " + name);}     }
}

一般情况,我们自己开发时很少自定义迭代器,因为java本身已经把迭代器做到内部中了

2Java中的迭代器

(1)Iterator接口

package java.util;import java.util.function.Consumer;public interface Iterator<E> {/**
* 如果迭代器又更多的元素,返回true。* 换句话说,如果next方法返回一个元素而不是抛出一个异常,则返回true。
*/
boolean hasNext();//返回迭代器中的下一个元素,如果没有,则抛出NoSuchElementException异常 E next();/**
* 从底层集合中删除该迭代器返回的最后一个元素(可选操作)。每执行一次next方法这个方法只能被调用1次。* 如果在迭代过程中,除了调用此方法之外,任何其他方法修改基础集合,则迭代器的行为是不确定的。 * 默认实现是抛出一个UnsupportedOperationException异常,不执行其他操作。 * 如果每次调用该方法前next方法没有执行,则抛出IllegalStateException异常。
*/
default void remove() {throw new UnsupportedOperationException("remove");}/**
*
@since 1.8(函数编程)。 * 对每个剩余元素执行给定的操作,直到所有元素都被处理或动作抛出异常为止。 * 如果指定了该顺序,则按迭代顺序执行操作。动作抛出的异常被传递给调用者。 * 如果action为null,则抛出NullPointerException
*/
default void forEachRemaining(Consumer<? super E> action) {Objects.requireNonNull(action);while (hasNext())action.accept(next());} }

 

首先,Iterator接口是属于java.util包的。然后里面只有4个方法(用法介绍请看注释)。

forEachRemaining方法是Java8函数编程新加入的。作用是对前游标之后的每个元素进行处理(没有返回值),具体怎么处理根据传入的函数。这项里操作使得迭代器更加灵活,操作粒度更加细致。

补充:default关键字可以让接口中的方法可以有默认的函数体,当一个类实现这个接口时,可以不用去实现这个方法,当然,这个类若实现这个方法,就等于子类覆盖了这个方法,最终运行结果符合Java多态特性。(Java8的新特性)

类注释:

/*** An iterator over a collection.  {@code Iterator} takes the place of {@link Enumeration} in the Java Collections Framework.  Iterators* differ from enumerations in two ways:** <ul>*      <li> Iterators allow the caller to remove elements from the underlying collection during the iteration with well-defined semantics.*      <li> Method names have been improved.* </ul>** <p>This interface is a member of the <a href="{@docRoot}/../technotes/guides/collections/index.html">Java Collections Framework</a>.*/

Iterator是集合上的迭代器。在Java集合框架中Iterator用来替代Enumeration,Iterator与Enumeration有以下两点区别:

  • Iterator允许调用者通过定于语义良好的迭代器删除底层集合中的元素。
  • 方法名称已得到改进。

这个接口是Java集合框架的成员。除了如上两点不同外,Java8版本Iterator还加入了函数式编程。

(2)Iterable接口

package java.lang;// 实现此接口,允许对象成为“for-each loop”语句的目标。
public interface Iterable<T> {
// 返回类型为 T元素的迭代器。Iterator<T> iterator();/*** @since 1.8
* 对Iterable的每个元素执行给定的操作,直到所有元素都被处理或动作引发异常。
* 除非实现类另有规定,否则按照迭代的顺序执行操作(如果指定了迭代顺序)。 动作抛出的异常被转发给调用者。
* 抛出NullPointerException - 如果指定的动作为空
*/default void forEach(Consumer<? super T> action) {Objects.requireNonNull(action);for (T t : this) {action.accept(t);}}/*** @since 1.8
* 在Iterable描述的元素上创建一个Spliterator。Spliterator继承了迭代器的fail-fast属性。
*
*/default Spliterator<T> spliterator() {return Spliterators.spliteratorUnknownSize(iterator(), 0);} }

Java集合包最常用的有Collection和Map两个接口的实现类。Map的实现类迭代器是内部实现的,而Collection继承了Iterable接口。

这里以ArrayList为例,梳理一下Iterator的工作流程。

ArrayList是Collection的子类,而Collection又实现了Iterable接口,Iterable接口里面有iterator()方法(该方法返回一个迭代器对象)。所以,ArrayList(或其父类)也必须实现iterator()方法。

iterator()方法返回一个Iterator对象,而前文中Iterator是个接口。我们不能不知道具体用哪个实现类也不能直接new接口,所以我们找到其直接父类AbstractList,查看iterator()到底如何实现的:

 

 public Iterator<E> iterator() {return new Itr();}private class Itr implements Iterator<E> {// Index of element to be returned by subsequent call to next.int cursor = 0;/*** Index of element returned by most recent call to next or* previous.  Reset to -1 if this element is deleted by a call* to remove.*/int lastRet = -1;/*** The modCount value that the iterator believes that the backing* List should have.  If this expectation is violated, the iterator* has detected concurrent modification.*/int expectedModCount = modCount;public boolean hasNext() {return cursor != size();}public E next() {checkForComodification();try {int i = cursor;E next = get(i);lastRet = i;cursor = i + 1;return next;} catch (IndexOutOfBoundsException e) {checkForComodification();throw new NoSuchElementException();}}public void remove() {if (lastRet < 0)throw new IllegalStateException();checkForComodification();try {AbstractList.this.remove(lastRet);if (lastRet < cursor)cursor--;lastRet = -1;expectedModCount = modCount;} catch (IndexOutOfBoundsException e) {throw new ConcurrentModificationException();}}final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}}

 

这里是通过内部类实现了Iterator接口,然后再将其实例对象返回。

 

原理很简单,每调用一次next方法,先返回当前游标指向位置的值,然后游标往下移动一位,直到游标数值等于list的size。

而在ArrayList类里面又提供了一个实现版本:

    /*** An optimized version of AbstractList.Itr*/private class Itr implements Iterator<E> {int cursor;       // index of next element to returnint lastRet = -1; // index of last element returned; -1 if no suchint expectedModCount = modCount;Itr() {}public boolean hasNext() {return cursor != size;}@SuppressWarnings("unchecked")public E next() {checkForComodification();int i = cursor;if (i >= size)throw new NoSuchElementException();Object[] elementData = ArrayList.this.elementData;if (i >= elementData.length)throw new ConcurrentModificationException();cursor = i + 1;return (E) elementData[lastRet = i];}public void remove() {if (lastRet < 0)throw new IllegalStateException();checkForComodification();try {ArrayList.this.remove(lastRet);cursor = lastRet;lastRet = -1;expectedModCount = modCount;} catch (IndexOutOfBoundsException ex) {throw new ConcurrentModificationException();}}@Override@SuppressWarnings("unchecked")public void forEachRemaining(Consumer<? super E> consumer) {Objects.requireNonNull(consumer);final int size = ArrayList.this.size;int i = cursor;if (i >= size) {return;}final Object[] elementData = ArrayList.this.elementData;if (i >= elementData.length) {throw new ConcurrentModificationException();}while (i != size && modCount == expectedModCount) {consumer.accept((E) elementData[i++]);}// update once at end of iteration to reduce heap write trafficcursor = i;lastRet = i - 1;checkForComodification();}final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}}

next方法很好理解,和父类大概是一个意思。remove方法是调用ArrayList.this.remove(lastRet)实现:

 

public E remove(int index) {rangeCheck(index);modCount++;E oldValue = elementData(index);int numMoved = size - index - 1;if (numMoved > 0)System.arraycopy(elementData, index+1, elementData, index,numMoved);elementData[--size] = null; // clear to let GC do its workreturn oldValue;}

 

numMoved 是计算要移动的元素个数(删除数组的某一位置的值,后面的值要依次往前移)。

cursor = lastRet;

lastRet = -1;

表示删除之后,游标前移1位。为什么这么做?举个例子,数组a = [1,2,3,4],若cursor = 2,游标指向数字3,则lastRet = 1。当删除a[1]的时候,a = [1,3,4]。3对应的位置变为1了,所以会有cursor = lastRet。

lastRet的值置为-1,这里很好的解释了前面注释中为什么remove方法一定要在next方法之后执行了。

 

转载于:https://www.cnblogs.com/ouym/p/8857448.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/280575.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

企业版Java EE正式易主 甲骨文再次放手

有人说甲骨文收购的东西大多没有了好下场&#xff0c;这么说虽然有些片面&#xff0c;但是最近一个月Java EE和Solaris的境遇难免让人产生类似的联想。 继笔者上次报道《甲骨文将放弃Java EE 开源基金会双手欢迎》之后&#xff0c;最新消息显示&#xff0c;原本在甲骨文手中的J…

js中各种位置

js中各种位置 js中有各种与位置相关的属性,每次看到的时候都各种懵逼。索性一次总结一下。 clientHeight 内容可视区域的高度。包括padding不包括border、水平滚动条、margin。对于inline的元素这个属性一直是0&#xff0c;单位px&#xff0c;只读元素。offsetHeight offsetHei…

如何判断您是否拥有32位或64位版本的Google Chrome浏览器

Google Chrome is extremely popular with our readers, but did you know that they also have a 64-bit version of the browser these days? Here’s how to tell which version you are running, and how to switch if you aren’t. 谷歌浏览器在我们的读者中非常受欢迎&a…

Kubernetes 跨集群流量调度实战 :访问控制

背景众所周知&#xff0c;Flomesh 的服务网格产品 osm-edge[1] 是基于 SMI&#xff08;Service Mesh Interface&#xff0c;服务网格接口&#xff09; 标准的实现。SMI 定义了流量标识、访问控制、遥测和管理的规范。在 上一篇 中&#xff0c;我们体验过了多集群服务&#xff0…

python下sqlite增删查改方法(转)

sqlite读写 #codingutf-8 import sqlite3 import os #创建数据库和游标 if os.path.exists( test.db):connsqlite3.connect( test.db)curconn.cursor() else:connsqlite3.connect( test.db)curconn.cursor()#创建表 cur.execute(CREATE TABLE IF NOT EXISTS customer (ID VARCH…

Apache HTTP Server 与 Tomcat 的三种连接方式介绍

本文转载自IBM developer 首先我们先介绍一下为什么要让 Apache 与 Tomcat 之间进行连接。事实上 Tomcat 本身已经提供了 HTTP 服务&#xff0c;该服务默认的端口是 8080&#xff0c;装好 tomcat 后通过 8080 端口可以直接使用 Tomcat 所运行的应用程序&#xff0c;你也可以将该…

印象笔记和有道云笔记程序员_记录,存储和共享笔记的最佳应用程序和云服务...

印象笔记和有道云笔记程序员Is your desk and computer covered with sticky notes? Do you have miscellaneous pieces of paper with bits of information buried in drawers, your laptop case, backpack, purse, etc.? Get rid of all the chaos and get organized with …

django15:中间件

中间件 开发django项目是&#xff0c;涉及全局相关功能&#xff0c;都可以使用中间件实现。 1.请求时&#xff0c;需要经过中间件&#xff0c;才能到达真正的django后端。 2.响应走的时候&#xff0c;也要经过中间件&#xff0c;才能出去。 依次经过里面的中间件进出&#x…

互联网算法和产品优化的几个反直觉现象

本文不涉及任何具体的业务和形态&#xff0c;没有公开任何数据和需要保护的技术。互联网产品和算法的优化&#xff0c;是广大程序员和产品经理的主要工作。但想准确衡量线上实验效果&#xff0c;从来都不简单。笔者将这些反直觉现象&#xff0c;总结成三个典型案例予以讨论。然…

SD 胡策 Round 1 T3 彩尾巴猹的二进制数

发现一个区间[L,R]代表的2进制数是3的倍数&#xff0c;当且仅当从L开始的后缀二进制值 - 从R1开始的后缀二进制值 是 3 的倍数 (具体证明因为太简单而被屏蔽)。 于是我们就可以在每个点维护从它开始的后缀二进制数的值&#xff0c;因为在%3同余系下只有3个数&#xff0c;所以我…

django16: csrf跨站请求伪造/CSRF相关装饰器

CSRF 即跨站请求攻击 跨站请求伪造csrf钓鱼网站本质搭建一个跟正常网站一模一样的页面用户在该页面上完成转账功能转账的请求确实是朝着正常网站的服务端提交唯一不同的在于收款账户人不同给用户书写form表单 对方账户的input没有name属性你自己悄悄提前写好了一个具有默认的…

dropbox_Google的新存储定价与Microsoft,Apple和Dropbox相比如何

dropboxGoogle’s subscription storage service has a new name: Google One. Some prices are dropping and customers will also get customer support from an actual human for the first time. Google的订阅存储服务有一个新名称&#xff1a;Google One。 一些价格正在下…

WPF效果第二百零六篇之快速黑白灰效果

一大早就看到群友讨论怎么快速让界面黑白灰效果,这不突然想起来N年前咱简单通过ShaderEffects调节过饱和度、对比度、亮度;今天再次玩耍一下;来看看最终实现的效果:1、核心代码&#xff1a;sampler2D implicitInput : register(s0); float factor : register(c0); float4 main(…

极大似然估计与贝叶斯定理

文章转载自&#xff1a;https://blog.csdn.net/zengxiantao1994/article/details/72787849 极大似然估计-形象解释看这篇文章&#xff1a;https://www.zhihu.com/question/24124998 贝叶斯定理-形象解释看这篇文章&#xff1a;https://www.zhihu.com/question/19725590/answer/…

艾媒:第三方应用商店形成BAT3争霸格局

iiMedia Research(艾媒咨询)近日发布的《2016Q2中国移动应用商店市场监测报告》&#xff0c;报告显示&#xff0c;2016年第二季度&#xff0c;第三方移动应用商店用户增长放缓&#xff0c;用户规模逐渐饱和。同时&#xff0c;随着豌豆荚宣布并入阿里移动事业群&#xff0c;中国…

layui 启用禁用_在不启用Apple Pay的情况下禁用烦人的Apple Pay通知

layui 启用禁用iPhone/iPad: Not interested in Apple Pay, and tired of seeing notifications about it? You can disable them, but the option is hidden. iPhone / iPad&#xff1a;对Apple Pay不感兴趣&#xff0c;又厌倦了看到有关它的通知&#xff1f; 您可以禁用它们…

数字孪生项目实战,WPF与Unity结合开发之路(一)

数字孪生项目实战&#xff0c;WPF与Unity结合开发之路&#xff08;一&#xff09;数字孪生项目实战&#xff0c;WPF与Unity结合开发之路&#xff08;一&#xff09;作 者&#xff1a;水娃嗨大家好&#xff0c;我是一名骨灰级的WPF开发者&#xff0c;我叫水娃。这次主要是向大…

idou老师教你学Istio06: 如何用istio实现流量迁移

流量迁移是流量管理的一个重要功能。istio提供的流量管理功能将流量从基础设施扩展中解耦&#xff0c;支持动态请求路由&#xff0c;故障注入、超时重试、熔断和流量迁移等。流量迁移的主要目的是将流量从微服务的某一版本的逐步迁移至另一个版本&#xff0c;如在新旧版本之间进…

用最少的代码,写一个完整MES项目(.NET6+WPF)

工业4.0时代&#xff0c;智能智造MES系统大行其道&#xff0c;然而基于.NET跨平台的罕见&#xff01;这里有一套《.NET6WPF企业级MES实战》教程&#xff0c;基于.NET6跨平台开发&#xff0c;实现了MES多核心功能&#xff0c;尤其是开发框架完整&#xff0c;非常适合复用。这里分…

django18:auth模块

Auth模块 执行数据库迁移命令后&#xff0c;自动生产多个表。 django_session auth_user 直接访问admin路由&#xff0c;需要输入用户名和密码&#xff0c;就是参考auth_user表 管理员用户才能进入 创建超级用户 createsuperuser from django.contrib import auth1.校验用…