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. Linux内核学习之2号进程kthreadd

    Author       : Toney Email         : vip_13031075266@163.com Date          : 2020.12.04 Copyright : ...

  2. Servlet生命周期和方法

    一.五个生命周期方法,有三个很重要,初始化方法.提供服务方法和销毁方法 1.三个主要方法 2.另外两个重写的成员方法只做了解 二.生命周期详解 其中,每次刷新页面都是一次对servlet访问: 页面访 ...

  3. 在 Docker 的 CentOS7 镜像 中安装 mysql

    在 Docker 的 CentOS7 镜像 中安装 mysql 本来以为是个很简单的过程居然折腾了这么久,之前部署云服务器时也没有好好地记录,因此记录下. 特别提醒:本文的操作环境是在 Docker ...

  4. 【C++基础教程】第二课

    一,上次的课后练习答案 1,输出1+2=3 2,输出2 2.25 2.25 2.25 3,第一空iostream或bits/stdc++.h 第二空main(),main(void)或main(int ...

  5. Django学习day10随堂笔记

    每日测验 """ 今日考题 1.默写ajax基本语法,及提交json数据和文件都需要添加哪些额外参数 2.什么是序列化,截止目前为止你所接触过的序列化有哪些 3.批量插入 ...

  6. 数学相关函数在PHP中的应用简介

    对于数学计算来说,最常见的其实还是我们使用各种操作符的操作,比如说 +加.-减 之类的.当然,PHP 中也为我们提供了一些可以方便地进行其他数学运算的操作函数.这些函数都属于 Math 扩展.这个扩展 ...

  7. vue报错 Uncaught TypeError: Cannot read property of null

    有可能是点击a标签,但是a标签有click事件,未阻止默认事件导致报错,开始都看不出来是什么错误

  8. 从浏览器渲染层面解析css3动效优化原理

    引言 在h5开发中,我们经常会需要实现一些动效来让页面视觉效果更好,谈及动效便不可避免地会想到动效性能优化这个话题: 减少页面DOM操作,可以使用CSS实现的动效不多出一行js代码 使用绝对定位脱离让 ...

  9. linux中如何查看文件上下文

    grep -C 10 keyword catalina.out filename https://blog.csdn.net/weixin_34791683/article/details/11660 ...

  10. docker启动jenikns,提示 :This image is for research only, DO NOT USE

    下载的jenkins镜像有问题?