Collection接口继承java.lang.Iterable接口,集合类重写了iterator()方法

public interface Iterable<T> {
Iterator<T> iterator(); default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
} default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
} public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}

原理:

一开始迭代器在所有元素的左边,调用next()之后,迭代器移到第一个和第二个元素之间,next()方法返回迭代器刚刚经过的元素。
hasNext()若返回True,则表明接下来还有元素,迭代器不在尾部。
remove()方法必须和next方法一起使用,功能是去除刚刚next方法返回的元素。

自己实现iterator遍历(参考JDK集合类):

import java.util.Iterator;

public class ForEachAPIDemo {
public static void main(String[] args) throws Exception {
Students students = new Students(10);
for (Student student : students) {
System.out.println(student.getSid() + ":" + student.getName());
}
}
} // 支持for each迭代循环的学生集合类
class Students implements Iterable<Student> {
// 存储所有学生类的数组
private Student[] students; // 该构造函数可以生成指定大小的学生类变量数组,并初始化该学生类变量数组
public Students(int size) {
students = new Student[size];
for (int i = 0; i < size; i++) {
students[i] = new Student(String.valueOf(i), "学生" + String.valueOf(i));
}
} @Override
public Iterator<Student> iterator() {
return new StudentIterator();
} // 实现Iterator接口的私有内部类,外界无法直接访问
private class StudentIterator implements Iterator<Student> {
// 当前迭代元素的下标
private int index = 0; // 判断是否还有下一个元素,如果迭代到最后一个元素就返回false
public boolean hasNext() {
return index != students.length;
} @Override
public Student next() {
return students[index++];
} // 这里不支持,抛出不支持操作异常
public void remove() {
throw new UnsupportedOperationException();
}
}
} class Student {
private String sid;
private String name; public Student(String sid, String name) {
setSid(sid);
setName(name);
} public String getSid() {
return sid;
} public void setSid(String sid) {
this.sid = sid;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
public String toString() {
return "Student{" +
"sid='" + sid + '\'' +
", name='" + name + '\'' +
'}';
}
}

foreach原理:

foreach语法最终被编译器转为了对Iterator.next()的调用

package test;

import java.util.List;

/**
* Created by vino on 2016/5/6.
*/
public class TestForeach {
List<Integer> integers;
public void testForeach(){
for(Integer i : integers){ }
}
} // 字节码
public void testForeach();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=3, args_size=1
0: aload_0
1: getfield #2 // Field integers:Ljava/util/List;
4: invokeinterface #3, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
9: astore_1
10: aload_1
11: invokeinterface #4, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
16: ifeq 32
19: aload_1
20: invokeinterface #5, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
25: checkcast #6 // class java/lang/Integer
28: astore_2
29: goto 10
32: return
LineNumberTable:
line 11: 0
line 13: 29
line 14: 32
LocalVariableTable:
Start Length Slot Name Signature
0 2 i Ljava/lang/Integer;
33 0 this Ltest/TestForeach;
}

Iterator对Collection 或 Map 进行迭代操作过程中是不允许修改 Collection / Map 的内容(add/remove)   允许set

可以使用 Iterator 本身的方法 remove() 来删除对象

原因:java中的fail-fast(快速失败)机制

撸代码解释(ArrayList为例):

1.ArrayList的属性modCount:add()  remove()时都会modCount++;

2.new Itr(implements Iterator)时属性expectedModCount=modCount,且next()是要检查expectedModCount ?= modCount;

3.调用ArrayList的add()  remove()时改变了modCount的值,下一次next()时检查expectedModCount != modCount,throw new ConcurrentModificationException();

4.Iterator 本身的方法 remove()删除元素之后会将modCount重新赋值给expectedModCount。

    // ArrayList:
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 work return oldValue;
} // 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; 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();
}
} final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}

参考资料:

为什么Iterator的remove方法可保证从源集合中安全地删除对象,而在迭代期间不能直接删除集合内元素

JDK源码学习笔记——Iterable/Iterator实现原理的更多相关文章

  1. JDK源码学习笔记——LinkedHashMap

    HashMap有一个问题,就是迭代HashMap的顺序并不是HashMap放置的顺序,也就是无序. LinkedHashMap保证了元素迭代的顺序.该迭代顺序可以是插入顺序或者是访问顺序.通过维护一个 ...

  2. JDK源码学习笔记——String

    1.学习jdk源码,从以下几个方面入手: 类定义(继承,实现接口等) 全局变量 方法 内部类 2.hashCode private int hash; public int hashCode() { ...

  3. JDK源码学习笔记——Integer

    一.类定义 public final class Integer extends Number implements Comparable<Integer> 二.属性 private fi ...

  4. JDK源码学习笔记——Enum枚举使用及原理

    一.为什么使用枚举 什么时候应该使用枚举呢?每当需要一组固定的常量的时候,如一周的天数.一年四季等.或者是在我们编译前就知道其包含的所有值的集合. 利用 public final static 完全可 ...

  5. JDK源码学习笔记——Object

    一.源码解析 public class Object { /** * 一个本地方法,具体是用C(C++)在DLL中实现的,然后通过JNI调用 */ private static native void ...

  6. JDK源码学习笔记——TreeMap及红黑树

    找了几个分析比较到位的,不再重复写了…… Java 集合系列12之 TreeMap详细介绍(源码解析)和使用示例 [Java集合源码剖析]TreeMap源码剖析 java源码分析之TreeMap基础篇 ...

  7. JDK源码学习笔记——HashMap

    Java集合的学习先理清数据结构: 一.属性 //哈希桶,存放链表. 长度是2的N次方,或者初始化时为0. transient Node<K,V>[] table; //最大容量 2的30 ...

  8. JDK源码学习笔记——ArrayList/Vector

    一.类定义 public class ArrayList<E> extends AbstractList<E> implements List<E>, Random ...

  9. JDK源码学习笔记——HashSet LinkedHashSet TreeSet

    你一定听说过HashSet就是通过HashMap实现的 相信我,翻一翻HashSet的源码,秒懂!! 其实很多东西,只是没有静下心来看,只要去看,说不定一下子就明白了…… HashSet 两个属性: ...

随机推荐

  1. linux 下 /bin /sbin 的区别 -- (转)

    /bin,/sbin,/usr/bin,/usr/sbin区别 /  : this is root directory                root 用户根目录 /bin : command ...

  2. c语言中的输入

    先打个白条有时间在写 c语言中输入一行回车之后,以空格为单位进行的分割

  3. 数字签名算法rsa

    数字签名算法消息传递模型 由消息发送方构建密钥对,这里由甲方完成. 由消息发送方公布公钥至消息接收方,这里由甲方将公钥公布给乙方. 注意如加密算法区别,这里甲方使用私钥对数据签名,数据与签名形成一则消 ...

  4. PKUWC 2019 自闭记

    PKUWC 2019 自闭记 Day -1 考前天天在隔壁的物竞教室划水(雀魂,能和吉老师一起玩的游戏都是好游戏),没有做题. Day 0 早上8:16的高铁,到广州南居然要6个小时...不知道福州和 ...

  5. JS中,children和childNodes的不同之处

    <ul id="ul"><li></li><li></li><li><span></spa ...

  6. win7下安装Linux实现双系统全攻略

    http://jingyan.baidu.com/article/c275f6bacc3326e33c756743.html

  7. dump_stack 实现分析【转】

    转自:http://kernel.meizu.com/2017/03/18-40-19-dump_stack.html 1 简介 说起 dump_stack() ,相信从事 Linux 内核或者驱动相 ...

  8. Linux内核通知链分析【转】

    转自:http://www.cnblogs.com/jason-lu/articles/2807758.html Linux内核通知链分析 1. 引言 Linux是单内核架构(monolithic k ...

  9. centos 6 编译安装php-5.4/5.5(lamp模式)

    在安装LAMP架构时,我们常用php-5.3的版本 现进行php-5.4/5.5的编译安装演示: [root@localhost ~]# cd /usr/local/src [root@localho ...

  10. jquery获取元素索引值index()的例子

    如果参数是一组DOM元素或者jQuery对象,那么返回值就是传递的元素相对于原先集合的位置. 如果参数是一个选择器,那么返回值就是原先元素相对于选择器匹配元素中的位置.如果找不到匹配的元素,则返回-1 ...