Enumeration<E>
在说Iterator之前就先说一下被代替的Enumeration<E>
Public interface enumerator<E>{
Public boolean hasMoreElements(); //是否还有元素
Public E nextElement(); //返回下一个元素
}
Enumeration 在1.0的时候就推出了在当时出现的的集合也是用Hashtable Vector也是用它来进行遍历的
在jdk1.2的时候就推出了Iterator代替他
直到Jdk1.5Enumeration才加入了泛型,但是也没能拯救他
Iterator(迭代器)和Enumeration(枚举类)区别
迭代器不仅可以读取集合中的数据还可以移除数据,而Enumeration只能遍历
迭代器的名字短
迭代器支持fail-fast机制,而Enumeration不支持
Enumeration本身是不支持同步的,但是在Voctor和Hashtable实现Enumeration时,添加了同步
Iterator是支持fail-fast机制的:当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。
迭代器Iterator(什么样的):
提供一种方法对一个容器对象中的元素进行访问,而有不暴露该对象容器的内部细节
在java中Itertor是一个接口,只提供了基本的规则
Public interface Iterator<E>{
Boolean hasNext();//判断是否有系一个元素
E next()//获取下一个元素
Void remove;//移除元素
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
Java还提供了一个接口Iterable(能够...样)
Public interface Iterable<T>{
Iterator<T> iterator();
default void forEach(Consumer<? super T> action) { //foreach循环
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
default Spliterator<T> spliterator() { //jdk1.8的新迭代器
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
Iterable接口实现后会返回一个迭代器iterator;常用的集合collection类都实现了这个接口.
Spliterator(splitable iterator可分割迭代器)接口是Java为了并行遍历数据源中的元素而设计的迭代器,这个可以类比最早Java提供的顺序遍历迭代器Iterator,但一个是顺序遍历,一个是并行遍历(等什么时候成为了dalao在研究这个都是些啥东西啊)
作用
返回一个标准的Iterable实现,实现Iterable接口了就可以通过foreach来遍历.在编译时会对foreach进行优化,转换为HashNext()和next()的调
为什么要在弄两个接口呢,直接实现Iterator不好吗
主要是为了迭代器之间不会相互干扰.
Iterator接口中的核心方法next(),hasNext(),remove(),都是依赖当前位置。如果这些集合直接实现Iterator接口,则势必导致集合对象中包含当前迭代位置的数据(指针)。当集合在不同方法间进行传递的时候,由于当前迭代位置不可知,所以next()的结果也不可知。除非再为Iterator接口添加一个reset()方法,用来重置当前迭代位置。 而当实现Iterable则不然,每次调用都返回一个从头开始的迭代器,各个迭代器之间互不影响。
在Iterator进行遍历的时候不能对正在遍历的容器进行改变大小的操作
看下Arraylist实现的Iterator
private class Itr implements Iterator<E> {
int cursor; // 表示下一个元素的索引位置
int lastRet = -1; // 表示上一个元素的索引位置
int expectedModCount = modCount;//预期被修改的次数
public boolean hasNext() {
return cursor != size; //当下一个索引的位置不等于元素个数的话 说明还有元素
}
@SuppressWarnings("unchecked")
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];
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
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();
}
}
}
fail-fast机制
集合的内部有一个modCount用来记录集合被修改的次数 而迭代器自己也有一个expectedModCount记录修改次数
根据最上面的成员属性 可以知道mod和expected刚开始是相同的 这时候如果遍历的时候直接用集合的方法修改集合那么mod和expected就会不同(checkForComodification) 就会抛出concurrxxxxx的异常
而如果是用迭代器自己的remove方法改动 那么两个将同时改变就不会报错
同时有个有趣的现象,ArrayList不用迭代器的方法删除倒数第二个元素也不是触发fail-fast机制
注意看那个hasNext方法
在删除倒数第二个元素的场景下:当倒数第二个元素时迭代完成,开始迭代最后一个元素时,此时cursor是4,size由于在迭代过程倒数第二个元素移除了,所以-1, 此时cursor和size相等,遍历结束,不会再进入下一个迭代,因此不会触发checkForComodification方法的fail-fast机制。
ListIterator
感觉Iterator不够强大,只能移除啊,好多功能没有
Java又出来一个新的接口,ListIterator接口,继承Iterator接口
双向遍历 可以在遍历期间修改集合,同时可以获取迭代器游标的在集合中的位置
遗憾的是,只有在list其子类才能使用
两个获取方式
List.listIterator()
List.listIterator(int location) 可以指定游标的位置
AbstractList是list的直接子类,就实现了ListIterator()方法,并且有两个内部迭代器实现类
SimpleListIterator,FullListIterator:
SimpleListIterator 的主要操作最后都交给子类来实现,List 的子类 ArrayList, LinkedList, Vector 由于底层实现原理不同(数组,双向链表),具体操作类实现有所不同。
FullListIterator 继承了 SimpleListIterator, SimpleListIterator 实现了 Iterator 接口:
Listlterator()返回的fulllistlterator();
ListIterator
Public ListIterator<E> listIterator(int location){
Return new fulllistiterator(location);
}
SimpleListIterator的源码
private class SimpleListIterator implements Iterator<E> {
//游标的位置,初始为 -1
int pos = -1;
//用来判断是否 fail-fast 的变量
int expectedModCount;
//记录上次迭代的位置
int lastPosition = -1;
SimpleListIterator() {
expectedModCount = modCount;
}
//当游标没有跑到最后一个元素后面时 hasNext 返回 true
public boolean hasNext() {
return pos + 1 < size();
}
//获取下一个元素
public E next() {
if (expectedModCount == modCount) {
try {
//获取游标后面的元素,具体子类有具体实现
E result = get(pos + 1);
//更新
lastPosition = ++pos;
return result;
} catch (IndexOutOfBoundsException e) {
throw new NoSuchElementException();
}
}
//当迭代时修改元素,就会报这个错
throw new ConcurrentModificationException();
}
//删除上次迭代操作的元素
public void remove() {
//还没进行迭代操作就会报这个错
if (this.lastPosition == -1) {
throw new IllegalStateException();
}
if (expectedModCount != modCount) {
throw new ConcurrentModificationException();
}
try {
//调用子类实现的删除操作
AbstractList.this.remove(lastPosition);
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
expectedModCount = modCount;
if (pos == lastPosition) {
pos--;
}
//每次删除后都会还原为 -1,也就是说我们迭代一次后只能 remove 一次,再 remove 就会报错
lastPosition = -1;
}
}
//FullListIterator源码
private final class FullListIterator extends SimpleListIterator implements ListIterator<E> {
//根据 start 指定游标位置
FullListIterator(int start) {
if (start >= 0 && start <= size()) {
pos = start - 1;
} else {
throw new IndexOutOfBoundsException();
}
}
//在游标前面添加元素
public void add(E object) {
if (expectedModCount == modCount) {
try {
//调用子类的添加操作,ArrayList, LinkedList,Vector 的添加操作实现有所不同
AbstractList.this.add(pos + 1, object);
} catch (IndexOutOfBoundsException e) {
throw new NoSuchElementException();
}
//游标后移一位
pos++;
//!注意! 添加后 上次迭代位置又变回 -1 了,说明 add 后调用 remove, set 会有问题!
lastPosition = -1;
if (modCount != expectedModCount) {
expectedModCount = modCount;
}
} else {
throw new ConcurrentModificationException();
}
}
//当游标不在初始位置(-1)时返回true
public boolean hasPrevious() {
return pos >= 0;
}
//游标后面的元素索引,就是游标 +1
public int nextIndex() {
return pos + 1;
}
//游标前面一个元素
public E previous() {
if (expectedModCount == modCount) {
try {
E result = get(pos);
lastPosition = pos;
pos--;
return result;
} catch (IndexOutOfBoundsException e) {
throw new NoSuchElementException();
}
}
throw new ConcurrentModificationException();
}
//游标前面元素的索引
public int previousIndex() {
return pos;
}
//更新之前迭代的元素为 object
public void set(E object) {
if (expectedModCount == modCount) {
try {
//调用子类的set
AbstractList.this.set(lastPosition, object);
} catch (IndexOutOfBoundsException e) {
throw new IllegalStateException();
}
} else {
throw new ConcurrentModificationException();
}
}
}