Java:Iterator接口与fail-fast小记

对 Java 中的 Iterator接口 和 fail-fast,做一个微不足道的小小小小记

Iterator

Iterator接口

Iterator:迭代器

迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。

Java 中的 Iterator 功能比较简单,并且只能单向移动:  

  1. 使用方法 public Iterator iterator() 要求容器返回一个 Iterator。第一次调用 Iterator 的 next() 方法时,它返回序列的第一个元素。注意:iterator() 方法是 java.lang.Iterable 接口,被 Collection 继承。  
  2. 使用 public E next() 获得序列中的下一个元素。 
  3. 使用 public boolean hasNext() 检查序列中是否还有元素。  
  4. 使用 default void remove() 将迭代器新返回的元素删除。

进一步:

迭代:即 Collection 集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。

public class IteratorDemo {
public static void main(String[] args) {
// 使用多态方式 创建对象
Collection<String> coll = new ArrayList<String>();
// 添加元素到集合
coll.add("串串星人");
coll.add("吐槽星人");
coll.add("汪星人");
// 遍历
// 使用迭代器 遍历 每个集合对象都有自己的迭代器
Iterator<String> it = coll.iterator();
// 泛型指的是 迭代出 元素的数据类型
while(it.hasNext()){ //判断是否有迭代元素
String s = it.next();//获取迭代出的元素
System.out.println(s);
}
}
}

注:在进行集合元素取出时,如果集合中已经没有元素了,还继续使用迭代器的next方法,将会发生java.util.NoSuchElementException没有集合元素的错误。

ListIterator

ListIterator 与 Iterator 的相同点:

  1. 都是迭代器,当需要对集合中元素进行遍历不需要干涉其遍历过程时,这两种迭代器都可以使用;

  2. ListIterator 实现了 Iterator 接口;

    public interface ListIterator<E> extends Iterator<E>

不同点:

  1. 使用范围不同:Iterator 可用来遍历 Set、List、Map集合,但是 ListIterator 只能用来遍历 List
  2. Iterator 对集合只能是前向遍历(hasNext(), Next()),ListIterator 既可以前向也可以后向(hasNext(), Next(), hasPrevious(), previous());
  3. ListIterator 可以定位当前索引的位置,nextIndex()previousIndex()可以实现。Iterator没有此功能。
  4. ListIterator 有 add 方法,可以向 List 中添加对象,而 Iterator 不能;
  5. 都可实现删除操作,但是 ListIterator 可以实现对象的修改,set()方法可以实现。Iterator仅能遍历,不能修改

总之:ListIterator 实现了 Iterator 接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引等等。

Enumeration

与 Enumeration 相比,Iterator 更加安全,因为当一个集合正在被遍历的时候,它会阻止其它线程去修改集合。否则会抛出 ConcurrentModificationException 异常。这其实就是 fail-fast 机制。具体区别有三点:

  1. Iterator 的方法名比 Enumeration 更科学;
  2. Iterator 有 fail-fast 机制,比 Enumeration 更安全
  3. Iterator 能够删除元素,Enumeration 并不能删除元素。

函数接口如下:

package java.util;

public interface Enumeration<E> {
boolean hasMoreElements();
E nextElement();
}
public interface Iterator<E> {
boolean hasNext();
E next();
void remove();
}

fail-fast

在上述 Iterator 中,介绍到 Enumeration 时,提到了 fail-fast 机制,在此做一点介绍

fail-fast 概述

fail-fast 的字面意思是“快速失败”。当我们在遍历集合元素的时候,经常会使用迭代器,但在迭代器遍历元素的过程中,如果集合的结构被改变的话,就会抛出异常,防止继续遍历。这就是所谓的快速失败机制。

fail-fast 迭代器抛出 ConcurrentModificationException,而 fail-safe 迭代器则不会。

结构上的改变:集合上的插入和删除就是结构上的改变;

对集合中某个元素进行修改的话,并不是结构上的改变;

抛出 ConcurrentModificationException 异常实例:

@Test
public void testFailFast(){
List<Integer> list = new ArrayList<>();
for(int i = 0; i < 20; i++){
list.add(i);
}
Iterator<Integer> it = list.iterator();
int temp = 0;
while(it.hasNext()){
if(temp == 3){
temp++;
list.remove(3); // 在迭代的过程中,改变了集合的结构,导致fail-fast
}else{
temp++;
System.out.println(it.next());
}
}
}

fail-fast 工作原理

public E next() {
checkForComodification();
...
} final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}

当迭代器在执行 next() 方法时,会调用 checkForComodification(),当modCount != expectedModCount 时则抛出异常,而当集合的结构发生变化时,modCount 就会发生改变,如:

// ArrayList 在添加元素时,modCount就会被改变
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
} private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
} private void ensureExplicitCapacity(int minCapacity) {
modCount++; // 这里修改了!!! // overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}

fail-fast 的一些处理方法:

如果我们不希望在迭代器遍历的时候因为并发等原因,导致集合的结构被改变,进而可能抛出异常的话,我们可以在涉及到会影响到 modCount 值改变的地方,加上同步锁(synchronized),或者直接使用 Collections.synchronizedList 来解决。

fail-safe

java.util 包中的所有集合类都被设计为 fail-fast 的,而 java.util.concurrent 中的集合类都为 fail-safe 的。

对于采用 fail-safe 机制来说,当检测到正在遍历的集合的结构被改变时,不会抛出异常;

这是因为,当集合的结构被改变的时候,fail-safe 机制会在复制原集合的一份数据出来,然后在复制的那份数据遍历。

因此,虽然fail-safe不会抛出异常,但存在以下缺点:

  1. 复制时需要额外的空间和时间上的开销。
  2. 不能保证遍历的是最新内容。

参考:

https://mp.weixin.qq.com/s/q1r9Pno6ANUzZ9wMzA-JSg

https://blog.csdn.net/xiangyuenacha/article/details/84253630

https://www.jianshu.com/p/bee159e0bd49

Java:Iterator接口与fail-fast小记的更多相关文章

  1. java Iterator接口

    Iterator主要遍历Collection集合中的元素,也有称为迭代器或迭代精灵. boolean hasNext():若被迭代的集合元素还没有被遍历,返回true. Object  next(): ...

  2. Fail Fast and Fail Safe Iterators in Java

    https://www.geeksforgeeks.org/fail-fast-fail-safe-iterators-java/ Fail Fast and Fail Safe Iterators ...

  3. java集合 之 Collection和Iterator接口

    Collection是List,Queue和Set接口的父接口,该接口里定义的方法即可用于操作Set集合,也可以用于List和Queue集合.Collection接口里定义了如下操作元素的方法. bo ...

  4. Java API ——Collection集合类 & Iterator接口

    对象数组举例: 学生类: package itcast01; /** * Created by gao on 15-12-9. */ public class Student { private St ...

  5. Java集合----概述、Collection接口、Iterator接口

    Java 集合概述 Java 集合就像一种容器,可以把多个对象的引用放入容器中. Java 集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系的关联数组 Java 集合可分为 Set.Li ...

  6. Java容器深入浅出之Collection与Iterator接口

    Java中用于保存对象的容器,除了数组,就是Collection和Map接口下的容器实现类了,包括用于迭代容器中对象的Iterator接口,构成了Java数据结构主体的集合体系.其中包括: 1. Co ...

  7. Java容器之Iterator接口

    Iterator 接口: 1. 所有实现了Collection接口的容器类都有一个iterator方法用以返回一个实现了Iterator接口的对象. 2. Iterator 对象称作迭代器,用以方便的 ...

  8. java中的Iterator接口

    Iterator接口 Iterator接口也是Java集合框架的成员,但它与Collection系列.Map系列的集合不一样:Collection系列集合.Map系列集合主要用于盛装其他对象,而Ite ...

  9. Java中的Enumeration、Iterable和Iterator接口详解

    前言 在看各类Java书籍或者博文的时候,总是会遇到Enumeration.Iterable和Iterator这三个接口,如果对这几个接口不是很明白的话,总会让自己看着看着就迷惑了,正好这周末,抽空把 ...

随机推荐

  1. 羽夏笔记——Win32(非WinAPI)

    写在前面   本笔记是由本人独自整理出来的,图片来源于网络.本人非计算机专业,可能对本教程涉及的事物没有了解的足够深入,如有错误,欢迎批评指正. 如有好的建议,欢迎反馈.码字不易,如果本篇文章有帮助你 ...

  2. Tomcat配置支持war包部署

    Tomcat配置支持war包部署 #cat /data/tomcat/conf/server.xml <?xml version='1.0' encoding='utf-8'?> < ...

  3. Docker安装Nginx(含:Windows启动、重启、停止)

    Docker安装Nginx #docker pull nginx:latest (第一次启动Docker-Nginx) #docker run --detach \ --publish 80:80 \ ...

  4. Maven专题3——生命周期与插件

    三套生命周期 Maven有3套相互独立的生命周期,用户可以调用某个生命周期的阶段,而不会对其他生命周期产生影响. 每个生命周期包含一些有先后顺序的阶段,后面的阶段依赖于前面的阶段,意味着用户调用后面的 ...

  5. Spring Boot中有多个@Async异步任务时,记得做好线程池的隔离!

    通过上一篇:配置@Async异步任务的线程池的介绍,你应该已经了解到异步任务的执行背后有一个线程池来管理执行任务.为了控制异步任务的并发不影响到应用的正常运作,我们必须要对线程池做好相应的配置,防止资 ...

  6. PHP中的那些魔术常量

    之前我们已经了解了一些常用的魔术方法,除了魔术方法外,PHP还提供一些魔术常量,相信大家在日常的工作中也都使用过,这里给大家做一个总结. 其实PHP还提供了很多常量但都依赖于各类扩展库,而有几个常量是 ...

  7. SourceTree使用详解-摘录收藏

    前言: 非原创,好文收录,原创作者:追逐时光者 俗话说的好工欲善其事必先利其器,Git分布式版本控制系统是我们日常开发中不可或缺的.目前市面上比较流行的Git可视化管理工具有SourceTree.Gi ...

  8. httprunner环境准备:Pycharm创建httprunner项目

    使用命令行方式,可能会不大习惯,下面来一个通过Pycharm来创建httprunner项目. 创建虚拟环境. 安装httprunner 创建脚手架目录:httprunner startproject ...

  9. git 报错 gitThere is no tracking information for the current branch. Please specify which branch you w

    新建本地分支后将本地分支推送到远程库, 使用git pull 或者 git push 的时候报错gitThere is no tracking information for the current ...

  10. Kafka与RocketMq文件存储机制对比

    一个商业化消息队列的性能好坏,其文件存储机制设计是衡量一个消息队列服务技术水平和最关键指标之一. 开头问题 kafka文件结构和rocketMQ文件结构是什么样子?特点是什么? 一.目录结构 Kafk ...