JDK源码学习笔记——Iterable/Iterator实现原理
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() 来删除对象
撸代码解释(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实现原理的更多相关文章
- JDK源码学习笔记——LinkedHashMap
HashMap有一个问题,就是迭代HashMap的顺序并不是HashMap放置的顺序,也就是无序. LinkedHashMap保证了元素迭代的顺序.该迭代顺序可以是插入顺序或者是访问顺序.通过维护一个 ...
- JDK源码学习笔记——String
1.学习jdk源码,从以下几个方面入手: 类定义(继承,实现接口等) 全局变量 方法 内部类 2.hashCode private int hash; public int hashCode() { ...
- JDK源码学习笔记——Integer
一.类定义 public final class Integer extends Number implements Comparable<Integer> 二.属性 private fi ...
- JDK源码学习笔记——Enum枚举使用及原理
一.为什么使用枚举 什么时候应该使用枚举呢?每当需要一组固定的常量的时候,如一周的天数.一年四季等.或者是在我们编译前就知道其包含的所有值的集合. 利用 public final static 完全可 ...
- JDK源码学习笔记——Object
一.源码解析 public class Object { /** * 一个本地方法,具体是用C(C++)在DLL中实现的,然后通过JNI调用 */ private static native void ...
- JDK源码学习笔记——TreeMap及红黑树
找了几个分析比较到位的,不再重复写了…… Java 集合系列12之 TreeMap详细介绍(源码解析)和使用示例 [Java集合源码剖析]TreeMap源码剖析 java源码分析之TreeMap基础篇 ...
- JDK源码学习笔记——HashMap
Java集合的学习先理清数据结构: 一.属性 //哈希桶,存放链表. 长度是2的N次方,或者初始化时为0. transient Node<K,V>[] table; //最大容量 2的30 ...
- JDK源码学习笔记——ArrayList/Vector
一.类定义 public class ArrayList<E> extends AbstractList<E> implements List<E>, Random ...
- JDK源码学习笔记——HashSet LinkedHashSet TreeSet
你一定听说过HashSet就是通过HashMap实现的 相信我,翻一翻HashSet的源码,秒懂!! 其实很多东西,只是没有静下心来看,只要去看,说不定一下子就明白了…… HashSet 两个属性: ...
随机推荐
- 项目记录 -- zpool set
zfs set <property=value> <filesystem|volume|snapshot> root@UA4300D-spa:~/hanhuakai/pro_0 ...
- TensorFlow中get_variable共享变量调用
import tensorflow as tf with tf.variable_scope('v_scope',reuse=True) as scope1: Weights1 = tf.get_va ...
- 【nginx】nginx的安装及测试
nginx中文文档:http://www.nginx.cn/doc/index.html 1.到官网下载nginx的压缩包: https://nginx.org/ 2.解压到相应的目录,比如我是e ...
- javaWeb面试题(重要)
1.Javaweb 技术的结构 1.1 技术结构图
- 使用BackgroundWorker
1,WPF应用程序为单线程模型(STAThread),所有UI控件都是主线程创建的,只有主线程能操作UI元素的显示. 2,其他工作线程要维护UI控件的显示,需调用主线程的Dispather,执行Inv ...
- mssql手工注入2
--+ 先说一些函数的说明: substring(str,start,len) 截取字符串的作用,第一个参数为要截取的字符串,第二个参数为从哪里开始截取,第三个参数为截取的长度 ascii(char) ...
- 【Sqlite3】sqlite_sequence表(转)
sqlite_sequence表也是SQLite的系统表.该表用来保存其他表的RowID的最大值.数据库被创建时,sqlite_sequence表会被自动创建.该表包括两列.第一列为name,用来存储 ...
- python基础===多线程
https://www.cnblogs.com/wj-1314/p/8263328.html threading 模块 先上代码: import time, threading def loop(): ...
- canvas写的地铁地图
更新: 18-9-21:填了个坑,更新了canvas绘制过程. 根据的是百度提供的坐标,canvas的坐标是大的坐标在后面,所以跟实际生活方向相反. 所以canvas里的北方在下方,实际生活中北方在上 ...
- LeetCode212. Word Search II
https://leetcode.com/problems/word-search-ii/description/ Given a 2D board and a list of words from ...