Iterable和Iterator
Iterator
接口位于的位置是java.util.Iterator
,它主要有两个抽象方法供子类实现。hasNext()
用来判断还有没有数据可供访问,next()
用来访问下一个数据。
集合Collection
不是直接去实现Iterator
接口,而是去实现Iterable
接口。
用这个Iterable
接口的iterator()
方法返回当前集合迭代器。
集合Collection
体系的集合都得按照使用iterator()
的方式返回可供遍历的迭代器iterator
。
集合使用Iterable
接口的iterator()
方法返回迭代器Iterator
有很多好处。
比如有两个线程都需要使用迭代器进行遍历集合的操作,如果通过直接实现Iterator
接口来拿迭代器。首先两者拿的是同一个迭代器,并且两者不同步,一个都遍历结束了,另一个都还没开始就无法遍历集合了,但是他本来循环从头开始遍历集合的所有数据的。
如果使用Iterable
接口的iterator()
方法返回迭代器Iterator
,那两者获得的就是不同迭代器了,就互不影响,自己可以按照自己的进度遍历集合就行。
for-each就是迭代器的语法糖,增强版的 for 循环(也叫 “for-each” 循环)在 Java 中是一个特性,它允许你遍历任何实现了 Iterable 接口的集合或者数组。
要使用增强型 for 循环(也称为 for-each 循环)遍历一个集合,集合的类需要实现 java.lang.Iterable
接口。Iterable 接口定义了一个方法 iterator(),它返回一个 Iterator 对象,用于遍历集合的元素。
import java.util.Iterator;
import java.util.NoSuchElementException;
// Iterable位于java.lang包下,不用显式import
public class MyCollection<T> implements Iterable<T> {
private T[] elements;
private int size;
@SuppressWarnings("unchecked")
public MyCollection(int capacity) {
elements = (T[]) new Object[capacity];
size = 0;
}
public void add(T element) {
if (size < elements.length) {
elements[size++] = element;
} else {
throw new IllegalStateException("Collection is full");
}
}
// 当一个类要实现 Iterable 接口,它必须提供一个 iterator() 方法,该方法返回一个 Iterator 对象。
@Override
public Iterator<T> iterator() {
return new MyIterator();
}
private class MyIterator implements Iterator<T> {
private int currentIndex = 0;
@Override
public boolean hasNext() {
return currentIndex < size;
}
@Override
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return elements[currentIndex++];
}
}
}
MyCollection<String> collection = new MyCollection<>(10);
collection.add("a");
collection.add("b");
collection.add("c");
// 使用增强型 for 循环
for (String element : collection) {
System.out.println(element);
}
// 和增强for循环等价的显式迭代器循环
// 当一个类实现了 Iterable 接口,它必须提供一个 iterator() 方法,该方法返回一个 Iterator 对象。
// 这个 Iterator 对象实现了 hasNext() 和 next() 方法,用于遍历集合中的元素。
// 下面的 iterator 是通过集合类实现的 Iterable 接口的 iterator() 方法获得的
Iterator<String> iterator = collection.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
System.out.println(element);
快速失败机制的工作原理
fail-fast 机制是java集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。
Java中的集合类(如ArrayList、LinkedList、HashMap等)使用快速失败机制来检测在迭代过程中对集合的结构性修改。
工作原理如下:
- modCount:每个集合类都有一个modCount字段,用于记录集合结构性修改的次数。结构性修改是指添加或删除元素,改变集合的大小。
- 迭代器的expectedModCount:当创建迭代器时,迭代器会保存当前集合的modCount值到一个名为expectedModCount的字段中。
- 一致性检查:在每次调用迭代器的
next()
或hasNext()
方法时,迭代器首先会检查modCount是否与expectedModCount相同。如果不相同,说明集合在迭代过程中的结构被修改过,迭代器会抛出ConcurrentModificationException
总的来说:只要是涉及了改变ArrayList元素的个数的方法都会导致modCount的改变。当多线程环境下,由于expectedModCount与modCount的改变不同步,导致两者之间不等,从而产生fast-fail。
这里的抛出异常,停止执行就是fail-fast,就是当自己遇到无法处理的情况时的处理方式。
下面是ConcurrentModificationException
异常的例子:
List<String> list = new ArrayList<>();
list.add("a");
Iterator<String> iterator = list.iterator(); // expectedModCount = modCount = 0
list.add("b"); // modCount = 1
iterator.next(); // expectedModCount != modCount, 抛出ConcurrentModificationException异常
for-each循环对集合进行增删也可能抛出异常,因为for-each在反编译下可以发现就是迭代器的语法糖,所以涉及到对迭代器的使用。
List<String> collection = new ArrayList<>();
collection.add("a");
collection.add("b");
// 使用增强型 for 循环
for (String element : collection) {
System.out.println(element);
}
// 和增强for循环等价的显式迭代器循环
// 当一个类实现了 Iterable 接口,它必须提供一个 iterator() 方法,该方法返回一个 Iterator 对象。
// 这个 Iterator 对象实现了 hasNext() 和 next() 方法,用于遍历集合中的元素。
// 下面的 iterator 是通过集合类实现的 Iterable 接口的 iterator() 方法获得的
Iterator<String> iterator = collection.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
if ("a".equals(element)) {
collection.remove(element);
}
}
想要在循环时进行增删操作,也就是进行这类对集合结构性有影响的操作,就要保证数据隔离性,下面是三种数据隔离性的处理方式,本质都是复制东西,只是复制的东西有所不同。
写入时复制(copy-on-write, 简称COW)。
GC 代表 “Garbage Collection”(垃圾回收)。COW读操作时并没有加锁,这是为了提高读操作的性能,但是有缺点,比如读数据的时候可能读不到最新的数据。例如,线程1往集合里面add数据才增加了一半,线程2这时候就去读数据,那读到的就还是老数据。
这样的话就只有增删才需要开辟一个新数组,其他情况都是使用原数组引用来读取原数组。
// 可以像使用普通的 ArrayList 一样使用 CopyOnWriteArrayList(写入时复制),并且可以通过 List 接口来引用它
List<String> cowList = new CopyOnWriteArrayList<>();
cowList.add("a");
cowList.add("b");
cowList.add("c");
Iterator<String> iterator = cowList.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
cowList.remove("b");
Iterator<String> iterator2 = cowList.iterator();
while (iterator2.hasNext()) {
System.out.println(iterator2.next());
}