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. shell循环之跳出循环

    1.break break命令允许跳出所有循环(终止执行后面的所有循环). 下面的例子中,脚本进入死循环直至用户输入数字大于5.要跳出这个循环,返回到shell提示符下,需要使用break命令. #! ...

  2. C语言中volatile、register、const、static、extern、 auto关键字的作用

    一.volatile详解 volatile的本意是"易变的" 因为访问寄存器要比访问内存单元快的多,所以编译器一般都会作减少存取内存的优化,但有可能会读脏数据.当要求使用volat ...

  3. fwm环境APP菜品数据加载失败的优化操作

    1)在项目的.env文件中添加如下一行: RESPONSE_CACHE_ENABLED=true 2)拷贝 laravel-worker.conf.example,将laravel字段替换为域名,并执 ...

  4. SpringBoot 如何生成接口文档,老鸟们都这么玩的!

    大家好,我是飘渺. SpringBoot老鸟系列的文章已经写了两篇,每篇的阅读反响都还不错,果然大家还是对SpringBoot比较感兴趣.那今天我们就带来老鸟系列的第三篇:集成Swagger接口文档以 ...

  5. Linux-实战常用命令

    目录 关机/重启/注销 系统信息和性能查看 磁盘和分区 ⽤户和⽤户组 ⽹络和进程管理 常⻅系统服务命令 ⽂件和⽬录操作 ⽂件查看和处理 打包和解压 RPM包管理命令 YUM包管理命令 DPKG包管理命 ...

  6. MySQL数据库初体验

    一.数据库的基本概念1.数据(Data) 描述事物的符号记录 包括数字,文字,图形,图像,声音,档案记录等 以"记录"形式按统一的格式进行存储 2.表 将不同的记录组织在一起 用来 ...

  7. 从线上日志统计接口访问量QPS

    这一阵子在面试,连续遇到好几家(大小厂都有)问我的项目线上qps的情况了,说实话,我作为一个大头兵,本来没关注过这个数据,只能含混地给个"大概.也许"的回答. 回来之后,我决定对业 ...

  8. Docker DevOps实战:GitLab+Jenkins(1)- GitLab容器搭建、使用SourceTree pull/push项目

    GitLab容器搭建 # 创建GitLab容器# --restart always #重启,容器自动重启# --privileged=true #容器内使用root权限 [root@localhost ...

  9. WireShark新手使用教程

    Wireshark是非常流行的网络封包分析软件,可以截取各种网络数据包,并显示数据包详细信息.常用于开发测试过程各种问题定位.本文主要内容包括: 1.Wireshark软件下载和安装以及Wiresha ...

  10. CF891B-Gluttony【构造】

    正题 题目链接:https://www.luogu.com.cn/problem/CF891B 题目大意 给出\(n\)个数字互不相同的一个序列\(a\),求它的一个排列\(b\),使得选出任意一个\ ...