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 两个属性: ...
随机推荐
- 利用procdump+Mimikatz 绕过杀软获取Windows明文密码
思路: 就是通过系统自带的procdump去下载存储用户名密码的文件(应该不能那么说这个文件,但是这样理解没问题),然后用猕猴桃读取. procdump.exe Procdump是一个轻量级的Sysi ...
- 仿照linux dpm机制,实现自己的dpm【转】
转自:http://blog.csdn.net/lixiaojie1012/article/details/23788713 前边我们讨论分析了linux 内核的dpm实现,分析的目的在于学以致用:在 ...
- Linq to SQL 小结
前天开始看这方面的资料,虽然看了网上对比 sql和linq的速度,万条数据可能要慢1/4左右的数度,但是介于的方便,还是学了 首先看看linq的基本语法: FROM XX IN DATASOURCE ...
- 【Java基础】一些问题
1. HashSet是如何保证数据不重复的: 首先,HashSet添加元素的时候,底层是通过HashMap的put方法来实现的,而添加的元素,则是保存在了hashMap的key里,因为HashMap的 ...
- ES6 promise简单实现
基本功能实现: function Promise(fn){ //需要一个成功时的回调 var doneCallback; //一个实例的方法,用来注册异步事件 this.then = function ...
- Leetcode 之Longest Common Prefix(34)
这题实现起来还是挺麻烦的,就偷懒使用下库函数strtod().第二个参数表示字符中不是数字的地方,如果后面是空格,则认为其仍是数字,否则不是. bool isNumber(char *s) { cha ...
- tinyhttpd ------ C 语言实现最简单的 HTTP 服务器
工作流程: 1>服务器启动,在指定端口或随机选取端口绑定httpd服务. 2>收到一个http请求时(其实就是listen端口accept的时候),派生一个线程运行accept_reque ...
- Nginx事件管理机制-epoll
epoll的最大好处在于他不会随着被监控描述符的数目的增长而导致效率极致下降. select是遍历扫描来判断每个描述符是否有事件发生,当监控的描述付越多时,时间消耗就越多,并且由于系统的限制selec ...
- [PAT] 1143 Lowest Common Ancestor(30 分)
1143 Lowest Common Ancestor(30 分)The lowest common ancestor (LCA) of two nodes U and V in a tree is ...
- Convert Sorted List to Binary Search Tree&&Convert Sorted Array to Binary Search Tree——暴力解法
Convert Sorted List to Binary Search Tree Given a singly linked list where elements are sorted in as ...