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

  1. public interface Iterable<T> {
  2. Iterator<T> iterator();
  3.  
  4. default void forEach(Consumer<? super T> action) {
  5. Objects.requireNonNull(action);
  6. for (T t : this) {
  7. action.accept(t);
  8. }
  9. }
  10.  
  11. default Spliterator<T> spliterator() {
  12. return Spliterators.spliteratorUnknownSize(iterator(), 0);
  13. }
  14. }
  15.  
  16. public interface Iterator<E> {
  17. boolean hasNext();
  18. E next();
  19. default void remove() {
  20. throw new UnsupportedOperationException("remove");
  21. }
  22. default void forEachRemaining(Consumer<? super E> action) {
  23. Objects.requireNonNull(action);
  24. while (hasNext())
  25. action.accept(next());
  26. }
  27. }

原理:

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

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

  1. import java.util.Iterator;
  2.  
  3. public class ForEachAPIDemo {
  4. public static void main(String[] args) throws Exception {
  5. Students students = new Students(10);
  6. for (Student student : students) {
  7. System.out.println(student.getSid() + ":" + student.getName());
  8. }
  9. }
  10. }
  11.  
  12. // 支持for each迭代循环的学生集合类
  13. class Students implements Iterable<Student> {
  14. // 存储所有学生类的数组
  15. private Student[] students;
  16.  
  17. // 该构造函数可以生成指定大小的学生类变量数组,并初始化该学生类变量数组
  18. public Students(int size) {
  19. students = new Student[size];
  20. for (int i = 0; i < size; i++) {
  21. students[i] = new Student(String.valueOf(i), "学生" + String.valueOf(i));
  22. }
  23. }
  24.  
  25. @Override
  26. public Iterator<Student> iterator() {
  27. return new StudentIterator();
  28. }
  29.  
  30. // 实现Iterator接口的私有内部类,外界无法直接访问
  31. private class StudentIterator implements Iterator<Student> {
  32. // 当前迭代元素的下标
  33. private int index = 0;
  34.  
  35. // 判断是否还有下一个元素,如果迭代到最后一个元素就返回false
  36. public boolean hasNext() {
  37. return index != students.length;
  38. }
  39.  
  40. @Override
  41. public Student next() {
  42. return students[index++];
  43. }
  44.  
  45. // 这里不支持,抛出不支持操作异常
  46. public void remove() {
  47. throw new UnsupportedOperationException();
  48. }
  49. }
  50. }
  51.  
  52. class Student {
  53. private String sid;
  54. private String name;
  55.  
  56. public Student(String sid, String name) {
  57. setSid(sid);
  58. setName(name);
  59. }
  60.  
  61. public String getSid() {
  62. return sid;
  63. }
  64.  
  65. public void setSid(String sid) {
  66. this.sid = sid;
  67. }
  68.  
  69. public String getName() {
  70. return name;
  71. }
  72.  
  73. public void setName(String name) {
  74. this.name = name;
  75. }
  76.  
  77. @Override
  78. public String toString() {
  79. return "Student{" +
  80. "sid='" + sid + '\'' +
  81. ", name='" + name + '\'' +
  82. '}';
  83. }
  84. }

foreach原理:

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

  1. package test;
  2.  
  3. import java.util.List;
  4.  
  5. /**
  6. * Created by vino on 2016/5/6.
  7. */
  8. public class TestForeach {
  9. List<Integer> integers;
  10. public void testForeach(){
  11. for(Integer i : integers){
  12.  
  13. }
  14. }
  15. }
  16.  
  17. // 字节码
  18. public void testForeach();
  19. descriptor: ()V
  20. flags: ACC_PUBLIC
  21. Code:
  22. stack=1, locals=3, args_size=1
  23. 0: aload_0
  24. 1: getfield #2 // Field integers:Ljava/util/List;
  25. 4: invokeinterface #3, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
  26. 9: astore_1
  27. 10: aload_1
  28. 11: invokeinterface #4, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
  29. 16: ifeq 32
  30. 19: aload_1
  31. 20: invokeinterface #5, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
  32. 25: checkcast #6 // class java/lang/Integer
  33. 28: astore_2
  34. 29: goto 10
  35. 32: return
  36. LineNumberTable:
  37. line 11: 0
  38. line 13: 29
  39. line 14: 32
  40. LocalVariableTable:
  41. Start Length Slot Name Signature
  42. 0 2 i Ljava/lang/Integer;
  43. 33 0 this Ltest/TestForeach;
  44. }

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。

  1. // ArrayList:
  2. public E remove(int index) {
  3. rangeCheck(index);
  4.  
  5. modCount++;
  6. E oldValue = elementData(index);
  7.  
  8. int numMoved = size - index - 1;
  9. if (numMoved > 0)
  10. System.arraycopy(elementData, index+1, elementData, index,
  11. numMoved);
  12. elementData[--size] = null; // clear to let GC do its work
  13.  
  14. return oldValue;
  15. }
  16.  
  17. // Itr:
  18. private class Itr implements Iterator<E> {
  19. int cursor; // index of next element to return
  20. int lastRet = -1; // index of last element returned; -1 if no such
  21. int expectedModCount = modCount;
  22.  
  23. public E next() {
  24. checkForComodification();
  25. int i = cursor;
  26. if (i >= size)
  27. throw new NoSuchElementException();
  28. Object[] elementData = ArrayList.this.elementData;
  29. if (i >= elementData.length)
  30. throw new ConcurrentModificationException();
  31. cursor = i + 1;
  32. return (E) elementData[lastRet = i];
  33. }
  34.  
  35. public void remove() {
  36. if (lastRet < 0)
  37. throw new IllegalStateException();
  38. checkForComodification();
  39.  
  40. try {
  41. ArrayList.this.remove(lastRet);
  42. cursor = lastRet;
  43. lastRet = -1;
  44. expectedModCount = modCount;
  45. } catch (IndexOutOfBoundsException ex) {
  46. throw new ConcurrentModificationException();
  47. }
  48. }
  49.  
  50. final void checkForComodification() {
  51. if (modCount != expectedModCount)
  52. throw new ConcurrentModificationException();
  53. }
  54. }

参考资料:

为什么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. 项目记录 -- zpool set

    zfs set <property=value> <filesystem|volume|snapshot> root@UA4300D-spa:~/hanhuakai/pro_0 ...

  2. TensorFlow中get_variable共享变量调用

    import tensorflow as tf with tf.variable_scope('v_scope',reuse=True) as scope1: Weights1 = tf.get_va ...

  3. 【nginx】nginx的安装及测试

    nginx中文文档:http://www.nginx.cn/doc/index.html 1.到官网下载nginx的压缩包: https://nginx.org/   2.解压到相应的目录,比如我是e ...

  4. javaWeb面试题(重要)

    1.Javaweb 技术的结构  1.1 技术结构图

  5. 使用BackgroundWorker

    1,WPF应用程序为单线程模型(STAThread),所有UI控件都是主线程创建的,只有主线程能操作UI元素的显示. 2,其他工作线程要维护UI控件的显示,需调用主线程的Dispather,执行Inv ...

  6. mssql手工注入2

    --+ 先说一些函数的说明: substring(str,start,len) 截取字符串的作用,第一个参数为要截取的字符串,第二个参数为从哪里开始截取,第三个参数为截取的长度 ascii(char) ...

  7. 【Sqlite3】sqlite_sequence表(转)

    sqlite_sequence表也是SQLite的系统表.该表用来保存其他表的RowID的最大值.数据库被创建时,sqlite_sequence表会被自动创建.该表包括两列.第一列为name,用来存储 ...

  8. python基础===多线程

    https://www.cnblogs.com/wj-1314/p/8263328.html threading 模块 先上代码: import time, threading def loop(): ...

  9. canvas写的地铁地图

    更新: 18-9-21:填了个坑,更新了canvas绘制过程. 根据的是百度提供的坐标,canvas的坐标是大的坐标在后面,所以跟实际生活方向相反. 所以canvas里的北方在下方,实际生活中北方在上 ...

  10. LeetCode212. Word Search II

    https://leetcode.com/problems/word-search-ii/description/ Given a 2D board and a list of words from ...