一、Iterator介绍
Java Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法,可用于迭代 ArrayList 和 HashSet 等集合。
Iterator 是 Java 迭代器最简单的实现,ListIterator 是 Collection API 中的接口, 它扩展了 Iterator 接口。
Iterator 类图
迭代器 it 的两个基本操作是 next 、hasNext 和 remove。
调用 it.next() 会返回迭代器的下一个元素,并且更新迭代器的状态。
调用 it.hasNext() 用于检测集合中是否还有元素。
调用 it.remove() 将迭代器返回的元素删除。
Iterator 类位于 java.util 包中,使用前需要引入它,语法格式如下:
1 import java.util.Iterator; // 引入 Iterator 类
二、Iterator使用
1、获取一个迭代器
集合想获取一个迭代器可以使用 iterator() 方法:
1 // 引入 ArrayList 和 Iterator 类 2 import java.util.ArrayList; 3 import java.util.Iterator; 4 5 public class RunoobTest { 6 public static void main(String[] args) { 7 8 // 创建集合 9 ArrayList<String> sites = new ArrayList<String>(); 10 sites.add("Google"); 11 sites.add("Runoob"); 12 sites.add("Taobao"); 13 sites.add("Zhihu"); 14 15 // 获取迭代器 16 Iterator<String> it = sites.iterator(); 17 18 // 输出集合中的第一个元素 19 System.out.println(it.next()); 20 } 21 }
2、循环集合元素
让迭代器 it 逐个返回集合中所有元素最简单的方法是使用 while 循环:
1 while(it.hasNext()) { 2 System.out.println(it.next()); 3 }
以下输出集合 sites 中的所有元素:
1 // 引入 ArrayList 和 Iterator 类 2 import java.util.ArrayList; 3 import java.util.Iterator; 4 5 public class RunoobTest { 6 public static void main(String[] args) { 7 8 // 创建集合 9 ArrayList<String> sites = new ArrayList<String>(); 10 sites.add("Google"); 11 sites.add("Runoob"); 12 sites.add("Taobao"); 13 sites.add("Zhihu"); 14 15 // 获取迭代器 16 Iterator<String> it = sites.iterator(); 17 18 // 输出集合中的所有元素 19 while(it.hasNext()) { 20 System.out.println(it.next()); 21 } 22 } 23 }
3、删除元素
要删除集合中的元素可以使用 remove() 方法。
以下实例我们删除集合中小于 10 的元素:
1 // 引入 ArrayList 和 Iterator 类 2 import java.util.ArrayList; 3 import java.util.Iterator; 4 5 public class RunoobTest { 6 public static void main(String[] args) { 7 ArrayList<Integer> numbers = new ArrayList<Integer>(); 8 numbers.add(12); 9 numbers.add(8); 10 numbers.add(2); 11 numbers.add(23); 12 Iterator<Integer> it = numbers.iterator(); 13 while(it.hasNext()) { 14 Integer i = it.next(); 15 if(i < 10) { 16 it.remove(); // 删除小于 10 的元素 17 } 18 } 19 System.out.println(numbers); 20 } 21 }
二、ArrayList中Iterator实现原理
最好先弄懂ArrayList实现原理:【Java】ArrayList 的实现原理
1、ArrayList 中 Interator 创建
如下:
1 public Iterator<E> iterator() { 2 return new Itr(); 3 }
它是创建了一个 ArrayList.Itr 对象
1 private class Itr implements Iterator<E> { 2 // 游标,默认为0,是下一个要返回元素的标 3 int cursor; // index of next element to return 4 // 最后返回元素的下标,默认为-1 5 int lastRet = -1; // index of last element returned; -1 if no such 6 // 期望的集合修改次数,默认为集合的修改次数 7 int expectedModCount = modCount; 8 9 10 Itr() {} 11 12 ...... 13 }
2、 Interator 的 hasNext() 方法
1 public boolean hasNext() { 2 // 游标不等于集合大小 3 return cursor != size; 4 }
3、 Interator 的 next() 方法
1 // 返回下一个元素 2 public E next() { 3 // 检查集合是否被修改 4 //同个期望的集合修改次数 是否相同比较,这里也就是集合fastfail机制 5 checkForComodification(); 6 // 需要返回元素的下标就是 cursor的值 7 int i = cursor; 8 if (i >= size) 9 throw new NoSuchElementException(); 10 Object[] elementData = ArrayList.this.elementData; 11 if (i >= elementData.length) 12 throw new ConcurrentModificationException(); 13 // 游标 + 1 14 cursor = i + 1; 15 // 给 lastRet 赋值,并且返回对应元素 16 return (E) elementData[lastRet = i]; 17 }
4、 Interator 的 remove() 方法
1 // 移除一个当前迭代元素 2 public void remove() { 3 if (lastRet < 0) 4 throw new IllegalStateException(); 5 // 检查集合是否被修改 6 checkForComodification(); 7 8 try { 9 // 调用集合对象的remove方法 10 ArrayList.this.remove(lastRet); 11 // 修改游标,指向当前位置 12 cursor = lastRet; 13 // 最后返回元素的下标,设为-1 14 lastRet = -1; 15 // 重新获取集合修改次数 16 expectedModCount = modCount; 17 } catch (IndexOutOfBoundsException ex) { 18 throw new ConcurrentModificationException(); 19 } 20 }
5、 Interator 的checkForComodification() 方法
1 // 检查集合是否被修改 2 // 同个期望的集合修改次数 是否相同比较,这里也就是集合fastfail机制 3 final void checkForComodification() { 4 if (modCount != expectedModCount) 5 throw new ConcurrentModificationException(); 6 }
期望的集合修改次数 是否被修改,这里也就是集合fast-fail机制
在ArrayList介绍中有这么一段话:
注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法:迭代器的快速失败行为应该仅用于检测 bug。
三、HashSet(HashMap)中Iterator实现原理
最好先弄懂HashMap实现原理:【Java】HashMap 的实现原理
1、HasSet 中 Interator 创建
1 // 创建iterator 2 public Iterator<E> iterator() { 3 // 使用hashmap中的keySet方法返回的keySet集合对象 4 // 通过keySet集合对象调用iterator(),返回迭代器 5 return map.keySet().iterator(); 6 }
由于HashSet底层使用的是HashMap,所有map.keySet(),返回的是hashMap对象的Set集合,如下:
1 // keySet() 方法 2 public Set<K> keySet() { 3 Set<K> ks = keySet; 4 if (ks == null) { 5 // 第一调用创建会去新建一个KeySet对象 6 ks = new KeySet(); 7 keySet = ks; 8 } 9 return ks; 10 }
而KeySet对象,的iterator() 方法,每次调用都会创建一个KeyIterator对象
1 final class KeySet extends AbstractSet<K> { 2 ....... 3 4 // 新建一个KeyIterator对象返回 5 public final Iterator<K> iterator() { return new KeyIterator(); } 6 7 ....... 8 }
KeyIterator对象继承了HashIterator
1 final class KeyIterator extends HashIterator 2 implements Iterator<K> { 3 public final K next() { return nextNode().key; } 4 }
查看HashIterator,方向迭代器基本实现在这里
1 abstract class HashIterator { 2 // 下一个要返回的节点 3 Node<K,V> next; // next entry to return 4 // 当前节点 5 Node<K,V> current; // current entry 6 // 期望集合修改次数 7 int expectedModCount; // for fast-fail 8 // 在数组中的当前槽位 9 int index; // current slot 10 11 // 构造方法 12 HashIterator() { 13 expectedModCount = modCount; 14 Node<K,V>[] t = table; 15 current = next = null; 16 index = 0; 17 // 当数组不空 且 集合大小大于0 18 if (t != null && size > 0) { // advance to first entry 19 // 找到第一个有数据的槽位 20 do {} while (index < t.length && (next = t[index++]) == null); 21 } 22 } 23 ....... 24 }
2、 Interator 的 hasNext() 方法
1 // 是否还有下一个元素 2 public final boolean hasNext() { 3 return next != null; 4 }
3、 Interator 的 next() 方法
1 // 返回下一个元素 2 public final K next() { return nextNode().key; } 3 4 // 下一个节点 5 final Node<K,V> nextNode() { 6 Node<K,V>[] t; 7 // e 指向 next 8 Node<K,V> e = next; 9 // 检查集合是否被修改 10 //同个期望的集合修改次数 是否相同比较,这里也就是集合fastfail机制 11 if (modCount != expectedModCount) 12 throw new ConcurrentModificationException(); 13 if (e == null) 14 throw new NoSuchElementException(); 15 // 判断下一个元素是否为空 16 if ((next = (current = e).next) == null && (t = table) != null) { 17 // 在数组中的当前槽位 + 1 18 // 找到下一个有数据的槽位 19 do {} while (index < t.length && (next = t[index++]) == null); 20 } 21 return e; 22 }
4、 Interator 的 remove() 方法
1 // 移除一个当前迭代元素 2 public final void remove() { 3 Node<K,V> p = current; 4 if (p == null) 5 throw new IllegalStateException(); 6 // 检查集合是否被修改 7 if (modCount != expectedModCount) 8 throw new ConcurrentModificationException(); 9 current = null; 10 K key = p.key; 11 // 调用hashMap中的removeNode 方法,移除元素 12 removeNode(hash(key), key, null, false, false); 13 // 重新获取集合修改次数 14 expectedModCount = modCount; 15 }
modCount集合修改次数,这里也就是集合fast-fail机制
在HashMap介绍中有这么一段话:
注意,迭代器的快速失败行为不能得到保证,一般来说,存在非同步的并发修改时,不可能作出任何坚决的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的做法是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。
fast-fail机制解决方案
造成fast-fail 原因,基本是多线程操作集合引起的,解决fast-fail问题,即使解决集合安全问题
1、方案一:集合安全问题,可以使用synchronzied关键字
2、方案二:使用Collections.synchronizedList()、Collections.synchronizedMap()
3、方案三:使用其他安全集合,比如:CopyOnWriteArrayList、ConcurrentHashMap
参考:https://www.runoob.com/java/java-iterator.html