一、iterator接口介绍

iterator接口,也是集合大家庭中的一员。和其他的MapCollection接口不同,iterator 主要是为了方便遍历集合中的所有元素,用于迭代访问集合中的元素,相当于定义了遍历元素的规范,而另外的MapCollection接口主要是定义了存储元素的规范。
还记得么?之前说的iterable接口,有一个方法就是叫iterator(),也是返回iterator对象。

迭代:不断访问集合中元素的方式,取元素之前先判断是否有元素,有则取出来,没有则结束,不断循环这个过程,直到遍历完里面所有的元素。

接口定义的方法如下:

boolean hasNext(); // 是否有下一个元素E next();   // 获取下一个元素// 移除元素
default void remove() {throw new UnsupportedOperationException("remove");}// 对剩下的所有元素进行处理,action则为处理的动作,意为要怎么处理
default void forEachRemaining(Consumer<? super E> action) {Objects.requireNonNull(action);while (hasNext())action.accept(next());}

但是值得注意的是,集合类的整体不是继承了iterator接口,而是继承了iterable接口,通过iterable接口的方法返回iterator的对象。值得注意的是,iteratorremove()方法,是迭代过程中唯一安全的修改集合的方法,为何这样说?
如果使用for循环索引的方式遍历,删除掉一个元素之后,集合的元素个数已经变化,很容易出错。例如

for(int i=0;i<collection.size();i++){if(i==2){collection.remove(i);}
}

iteratorremove()方法则不会出错,因为通过调用hasNext()next()方法,对指针控制已经处理得比较完善。

二、为什么需要iterator接口

首先,我们知道iterator接口是为了定义遍历集合的规范,也是一种抽象,把在不同集合的遍历方式抽象出来,这样遍历的时候,就不需要知道不同集合的内部结构。

为什么需要抽象?

假设没有iterator接口,我们知道,遍历的时候只能通过索引,比如

for(int i=0;i<array.size();i++){T item = array[i];
}

这样一来,耦合程度比较高,如果使用的数据结构变了,就要换一种写法,不利于维护已有的代码。如果没有iterator,那么客户端需要维护指针,相当于下放了权限,会造成一定程度的混乱。抽象则是把遍历功能抽取出来,交给iterator处理,客户端处理集合的时候,交给更“专业”的它,it do it well.

三、iterator接口相关接口

3.1 ListIterator

ListIterator继承于Iterator接口,功能更强大,只能用于访问各种List类型,使用List类型的对象list,调用listIterator()方法可以获取到一个指向list开头的ListIterator

从上面图片接口看,这个接口具有访问下一个元素,判断是否有下一个元素,是否有前面一个元素,判断是否有前一个元素,获取下一个元素的索引,获取上一个元素的索引,移除元素,修改元素,增加元素等功能。和普通的Iterator不一样的是,ListIterator的访问指针可以向前或者向后移动,也就是双向移动。

boolean hasNext();  //是否还有元素 E next();   //获取下一个元素boolean hasPrevious();  //是否有上一个元素E previous();   // 获取上一个元素int nextIndex();    //获取下一个索引int previousIndex();    //获取上一个索引void remove();  //移除void set(E e); //更新void add(E e); //添加元素

测试代码如下:

        List<String> list =new ArrayList<String>(Arrays.asList("Book","Pen","Desk"));// 把指针指向第一个元素ListIterator<String> lit = list.listIterator(1);while(lit.hasNext()){System.out.println(lit.next());}System.out.println("===================================");//指针指向最后一个元素列表中的最后一个元素修改ChangeDesk。lit.set("ChangeDesk");// 往前面遍历while(lit.hasPrevious()){System.out.println(lit.previous());}

输出如下:

Pen
Desk
===================================
ChangeDesk
Pen
Book

如果点开ArrayList的源码,看到与ListIterator相关的部分,我们会发现其实ArrayList在底层实现了一个内部类ListItr,继承了Itr,实现了ListIterator接口。这个Itr其实就是实现了Iterator,实现了基本的List迭代器功能,而这个ListItr则是增强版的专门为List实现的迭代器。里面使用cursor作为当前的指针(索引),所有函数功能都是操作这个指针实现。

private class ListItr extends Itr implements ListIterator<E> {ListItr(int index) {super();// 设置当前指针 cursor = index;}public boolean hasPrevious() {// 不是第一个元素就表明有前一个元素return cursor != 0;}// 获取下一个元素索引public int nextIndex() {return cursor;}// 获取前面一个元素索引public int previousIndex() {return cursor - 1;}@SuppressWarnings("unchecked")public E previous() {//检查是否被修改checkForComodification();int i = cursor - 1;if (i < 0)throw new NoSuchElementException();Object[] elementData = ArrayList.this.elementData;if (i >= elementData.length)throw new ConcurrentModificationException();cursor = i;// 返回前一个元素return (E) elementData[lastRet = i];}public void set(E e) {if (lastRet < 0)throw new IllegalStateException();checkForComodification();try {ArrayList.this.set(lastRet, e);} catch (IndexOutOfBoundsException ex) {throw new ConcurrentModificationException();}}public void add(E e) {checkForComodification();try {int i = cursor;ArrayList.this.add(i, e);cursor = i + 1;lastRet = -1;expectedModCount = modCount;} catch (IndexOutOfBoundsException ex) {throw new ConcurrentModificationException();}}}

我们可以看到,在上面方法中,有很多校验,比如checkForComodification(),意为检查是否被修改,list中的元素修改有可能导致数组越界。

3.2 SpitIterator

准确地来说,SpitIteratorIterator并没有什么关系,只是两个功能上有类似。SpitIterator主要是定义类将集合分割成多个集合,方便并行计算。

3.2.1 SpitIterator源码方法解析

public interface Spliterator<T> {// 顺序处理每一个元素,参数是处理的动作,如果还有元素需要处理则返回true,否则返回falseboolean tryAdvance(Consumer<? super T> action);// 依次处理剩下的元素default void forEachRemaining(Consumer<? super T> action) {do { } while (tryAdvance(action));}// 最重要的方法,用来分割集合Spliterator<T> trySplit();//估算还有多少元素需要遍历处理long estimateSize();// 获取准确的元素,如果不能获取准确的,则会返回估算的default long getExactSizeIfKnown() {return (characteristics() & SIZED) == 0 ? -1L : estimateSize();}// 表示该Spliterator有哪些特性,这个像是个拓展功能,更好控制和优化Spliterator使用int characteristics();// 判断是否有哪些特性default boolean hasCharacteristics(int characteristics) {return (characteristics() & characteristics) == characteristics;}// 如果这个Spliterator的源具有已排序的特征,那么这个方法将返回相应的比较器。如果源按自然顺序排序,则返回     // null。否则,如果源未排序,则抛出IllegalStateException。default Comparator<? super T> getComparator() {throw new IllegalStateException();}public static final int ORDERED    = 0x00000010;public static final int DISTINCT   = 0x00000001;public static final int SORTED     = 0x00000004;public static final int SIZED      = 0x00000040;public static final int NONNULL    = 0x00000100;public static final int IMMUTABLE  = 0x00000400;public static final int CONCURRENT = 0x00001000;public static final int SUBSIZED = 0x00004000;
}

使用的方法例子如下:

    public static void spliterator(){List<String> list = Arrays.asList("1", "2", "3","4","5","6","7","8","9","10");// 获取可迭代器Spliterator<String> spliterator = list.spliterator();// 一个一个遍历System.out.println("tryAdvance: ");spliterator.tryAdvance(item->System.out.print(item+" "));spliterator.tryAdvance(item->System.out.print(item+" "));System.out.println("n-------------------------------------------");// 依次遍历剩下的System.out.println("forEachRemaining: ");spliterator.forEachRemaining(item->System.out.print(item+" "));System.out.println("n------------------------------------------");// spliterator1:0~10Spliterator<String> spliterator1 = list.spliterator();// spliterator1:6~10 spliterator2:0~5Spliterator<String> spliterator2 = spliterator1.trySplit();// spliterator1:8~10 spliterator3:6~7Spliterator<String> spliterator3 = spliterator1.trySplit();System.out.println("spliterator1: ");spliterator1.forEachRemaining(item->System.out.print(item+" "));System.out.println("n------------------------------------------");System.out.println("spliterator2: ");spliterator2.forEachRemaining(item->System.out.print(item+" "));System.out.println("n------------------------------------------");System.out.println("spliterator3: ");spliterator3.forEachRemaining(item->System.out.print(item+" "));}

  • tryAdvance() 一个一个元素进行遍历
  • forEachRemaining() 顺序地分块遍历
  • trySplit()进行分区形成另外的 Spliterator,使用在并行操作中,分出来的是前面一半,就是不断把前面一部分分出来

结果如下:

tryAdvance:
1 2
-------------------------------------------
forEachRemaining:
3 4 5 6 7 8 9 10
------------------------------------------
spliterator1:
8 9 10
------------------------------------------
spliterator2:
1 2 3 4 5
------------------------------------------
spliterator3:
6 7

还有一些其他的用法在这里就不列举了,主要是trySplit()之后,可以用于多线程遍历。理想的时候,可以平均分成两半,有利于并行计算,但是不是一定平分的。

3.2.2 SpitIterator里面哪些特征常量有什么用呢?

spliterator可以将其实现特征表示为同一接口中定义的一组常量。也就是我们见到的ORDERED,DISTINCT,SORTED,SIZED之类的,这个意思是每一个实现类,都有自己的实现方式,实现方式不同,实现特征也不一样,比如ArrayList实现特征是ORDERED,SIZEDSUBSIZED,这个我们可以通过 characteristics() and hasCharacteristics()来判断。例如:

    public static void main(String[] args) throws Exception{List<String> list = new ArrayList<>();Spliterator<String> s = list.spliterator();System.out.println(s.characteristics());if(s.hasCharacteristics(Spliterator.ORDERED)){System.out.println("ORDERED");}if(s.hasCharacteristics(Spliterator.DISTINCT)){System.out.println("DISTINCT");}if(s.hasCharacteristics(Spliterator.SORTED)){System.out.println("SORTED");}if(s.hasCharacteristics(Spliterator.SIZED)){System.out.println("SIZED");}if(s.hasCharacteristics(Spliterator.CONCURRENT)){System.out.println("CONCURRENT");}if(s.hasCharacteristics(Spliterator.IMMUTABLE)){System.out.println("IMMUTABLE");}if(s.hasCharacteristics(Spliterator.NONNULL)){System.out.println("NONNULL");}if(s.hasCharacteristics(Spliterator.SUBSIZED)){System.out.println("SUBSIZED");}}

输出的结果是

16464
ORDERED
SIZED
SUBSIZED

输出结果中的16464和其他的怎么挂钩的呢?其实我们发现上面的hasCharacteristics()方法中,实现是return (characteristics() & characteristics) == characteristics;,不难看出,这些状态是根据与运算来计算出来的。上面的结果也表明ArrayListORDERED,SIZEDSUBSIZED这几个特征。
如果是HashSet则特征是DISTINCTSIZED

四、 iterator在集合中的实现例子

iterator只是一个接口,相当于一个规范,所有的子类或者继承类实现的时候理论上应该遵守,但是不一样的继承类/子类会有不一样的实现。

4.1 iterator在ArrayList的实现

iterator只是一个接口,一个规范,虽然里面有个别方法有默认实现,但是最重要也最丰富的的,是它在子类中的实现与拓展,现在来看在ArrayList 中的实现。ArrayList并没有直接去实现iterator接口,而是通过内部类的方式来操作,内部类为Itr,

    private class Itr implements Iterator<E> {// 下一个元素的索引(指针)int cursor;       // index of next element to return// 最后一个元素指针索引int lastRet = -1; // index of last element returned; -1 if no such// 修改次数(版本号)int 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();}}

从上面的源码可以看到,很多关于被修改的检查,集合会追踪修改(增删改)的次数(modCount 又称版本号),每一个迭代器会单独立维护一个计数器,在每次操作(增删改),检查版本号是否发生改变,如果改变,就会抛出ConcurrentModificationException() 异常,这是一种安全保护机制。
安全检查,快速失败机制实现主要和变量modCountexpectedModCount,以及一个checkForComodification()方法有关,也就是expectedModCount是内部类的修改次数,从字面意思看是指理论上期待的修改次数,modCount是外部类的修改次数,创建的时候,会将modCount赋值给expectedModCount,两者保持一致,如果在迭代的过程中,外部类的modCount对不上expectedModCount,n那么就会抛出ConcurrentModificationException异常。

4.2 iterator在HashMap的实现

首先,HashMap里面定义了一个HashIterator,为什么这样做呢?因为HashMap存储结构的特殊性,里面有Entry<key,value>,所以遍历就有三种情况,一个是Key,一个是Value,另一个就是Entry,这三个的迭代遍历都有相似性,所以这里根据抽象原则,定义了一个Hash迭代器。

    abstract class HashIterator {// 下一个节点Node<K,V> next;// 当前节点Node<K,V> current;     // current entry// 期望修改次数int expectedModCount;  // for fast-fail// 索引int index;             // current slotHashIterator() {expectedModCount = modCount;Node<K,V>[] t = table;current = next = null;index = 0;if (t != null && size > 0) { // 指向第一个不为空的元素do {} while (index < t.length && (next = t[index++]) == null);}}// 是否有下一个节点public final boolean hasNext() {return next != null;}// 获取下一个节点final Node<K,V> nextNode() {Node<K,V>[] t;Node<K,V> e = next;if (modCount != expectedModCount)throw new ConcurrentModificationException();if (e == null)throw new NoSuchElementException();if ((next = (current = e).next) == null && (t = table) != null) {do {} while (index < t.length && (next = t[index++]) == null);}return e;}// 移除public final void remove() {Node<K,V> p = current;if (p == null)throw new IllegalStateException();if (modCount != expectedModCount)throw new ConcurrentModificationException();current = null;K key = p.key;removeNode(hash(key), key, null, false, false);expectedModCount = modCount;}}

之后分别定义KeyIterator,ValueIterator,EntryIterator,继承于HashIterator

// 遍历keyfinal class KeyIterator extends HashIteratorimplements Iterator<K> {public final K next() { return nextNode().key; }}// 遍历valuefinal class ValueIterator extends HashIteratorimplements Iterator<V> {public final V next() { return nextNode().value; }}//遍历entryfinal class EntryIterator extends HashIteratorimplements Iterator<Map.Entry<K,V>> {public final Map.Entry<K,V> next() { return nextNode(); }}

五、总结

以上的种种,关于Iterator,其实就是一个迭代器,可简单地理解为遍历使用,主要功能是指向一个节点,向前或者向后移动,如果数据结构复杂就需要多个迭代器,比如HashMap,可以避免多个迭代器之间相互影响。每一个迭代器都会有 expectedModCount 和modCount,就是校验这个迭代过程中是否被修改,如果修改了,则会抛出异常。

【作者简介】: 秦怀,公众号【秦怀杂货店】作者,技术之路不在一时,山高水长,纵使缓慢,驰而不息。这个世界希望一切都很快,更快,但是我希望自己能走好每一步,写好每一篇文章,期待和你们一起交流。

此文章仅代表自己(本菜鸟)学习积累记录,或者学习笔记,如有侵权,请联系作者核实删除。人无完人,文章也一样,文笔稚嫩,在下不才,勿喷,如果有错误之处,还望指出,感激不尽~

- END -

collection集合 多少钱_Java 集合(2)-- Iterator接口源码超级详细解析相关推荐

  1. collection集合 多少钱_Java集合框架大汇总,建议收藏

    Java集合 Java集合框架:是一种工具类,就像是一个容器可以存储任意数量的具有共同属性的对象. Java集合中成员很丰富,常用的集合有ArrayList,HashMap,HashSet等.线程安全 ...

  2. 【图像重建】基于matlab布雷格曼迭代算法集合ART算法CT图像重建【含Matlab源码 1905期】

    一.获取代码方式 获取代码方式1: 完整代码已上传我的资源:[图像重建]基于matlab布雷格曼迭代算法集合ART算法CT图像重建[含Matlab源码 1905期] 获取代码方式2: 通过订阅紫极神光 ...

  3. 管中窥豹:由例子切入ArrayList及其Iterator的源码实现

    管中窥豹:由例子切入ArrayList及其Iterator的源码实现 文章目录 管中窥豹:由例子切入ArrayList及其Iterator的源码实现 一个例子 ArrayList的add(E e)方法 ...

  4. 秒赞网源码java_LinkedList与ArrayList通过Iterator迭代器源码效率探究

    前言先来点屁话,最近发觉每周小结效益很低,不适于博文,因此之后不写每周小结,多写一些干货跟学习记录,个人觉得这么更适于我. 最近在学习<数据结构与算法分析-java语言描述>这本书,书的3 ...

  5. java集合框架介绍_Java集合框架介绍

    Java集合框架介绍 发布时间:2020-06-26 17:00:14 来源:亿速云 阅读:85 作者:Leah 这篇文章将为大家详细讲解有关Java集合框架介绍,文章内容质量较高,因此小编分享给大家 ...

  6. java集合框架类_Java集合框架总结—超详细-适合面试

    Set和List接口是Collection接口派生的两个子接口,Queue是Java提供的队列实现,类似于List. Map实现类用于保存具有映射关系的数据(key-value). 2.Set.Lis ...

  7. java如何获得集合的长度_Java集合长度

    Java 实例 - 集合长度 以下实例演示了如何使用 Collections 类 的collection.add() 来添加数据并使用 collection.size()来计算集合的长度: Main. ...

  8. java集合框架介绍_Java集合框架全面介绍(一)

    Java平台提供了一个全新的集合框架."集合框架"主要由一组用来操作对象的接口组成.不同接口描述一组不同数据类型. Java 2集合框架图 集合接口:6个接口(短虚线表示),表示不 ...

  9. java集合set初始化_Java集合--Set(基础)

    1.Set 上一篇,我们介绍Java中的List集合.本篇,让我们继续学习,来了解下Set集合: Set继承于Collection接口,是一个不允许出现重复元素,并且无序的集合,主要有HashSet和 ...

最新文章

  1. 在Ubuntu 14.04 64bit上安装百度云Linux客户端BCloud
  2. Ubuntu14.04下切换系统自带的Python和Anaconda 下的Python
  3. 【结果很简单,过程很艰辛】记阿里云Ons消息队列服务.NET接口填坑过程
  4. netty : Max frame length of 65536 has been exceeded.
  5. 树莓派打造mini广播(FM)系统
  6. 一些自用的编程工具推荐
  7. 手机号码归属地数据库下载
  8. office2016风格后台管理系统html模板下载-uimaker设计
  9. 软件测试 — 面试题
  10. 巧做“士兵突击”的个性日历
  11. 从交互设计“流行元素”中启发
  12. 小白学 Python(1):开篇
  13. 使用Echarts将Excel数据转换成色彩地图HTML小工具
  14. 以DA方式通过opcdaauto.dll访问opc服务器异步操作异常0x80040200问题
  15. 离散数学图论经典问题之握手定理
  16. 2022年电脑行业前景
  17. 使用posman访问post请求
  18. 怎样给黑白照片上色?2个技能教你如何给黑白照片上色
  19. NS2 教學手冊 ( NS2 Learning Guide)
  20. tilemap之基础使用

热门文章

  1. win7 linux 共享文件夹权限设置,samba 配置共享 win7 无权限访问
  2. mysql 时区设定_如何设置MySQL 时区
  3. mac os 安装linux驱动,教程:Ubuntu 14.04安装Mac OS X主题
  4. 需求获取安排计划书_创业型公司商业计划书PPT怎么写?才是投资人最想看的,免费领取...
  5. C++ new一个数组方法和初始化的方法
  6. 小白实操ESP8266AT固件烧录,版本1.7.1,希望对爱玩ESP8266的开发小伙伴有帮助!
  7. python怎么写多行_python 多行字符串怎么写才能不破坏缩进
  8. 段错误 php,php扩展出现段错误怎么办
  9. 人才短缺、成本高昂,制造企业智能化转型路径如何破局?
  10. 云原生架构下的持续交付实践