标签:type 解释 就是 cep 情况 组合 安全 防止 自己实现
最近去某公司面试,被问到了一个简单的问题,ArrayList中要删除一些元素应该怎么操作?答曰:"使用Iterator迭代器遍历,判断之后再用迭代器提供的remove()方法将判断为true的元素删掉",问:“为什么要选择这个方法?”答曰:“迭代器采用的是fail-fast机制,foreach循环内部调用了迭代器,删除元素破坏了集合的结构,所以会报错”,追问:“为什么不能用for循环呢?没使用迭代器也应该可行啊?为什么迭代器又是可以的呢?” 答曰:“因为for循环删掉元素后会错位,至于迭代器是怎么解决这个问题的, 还真不知道...”
作为一名菜鸟,招架不了这么连珠炮般的提问,于是回去读了一遍关于Iterator的源码,简要的解析一番。本次学习过程大概是围绕这几个问题展开的:
1、Iterator和Iterable的关系
Iterator是java中的一个接口,定义了一系列方法。诸如hasNext()判定、next()取下一元素的值和remove()删除当前元素值。
2、fail-fast机制
The iterators returned by this class‘s {@link #iterator() iterator} and * {@link #listIterator(int) listIterator} methods are <em>fail-fast</em>:</a> * if the list is structurally modified at any time after the iterator is * created, in any way except through the iterator‘s own * {@link ListIterator#remove() remove} or * {@link ListIterator#add(Object) add} methods, the iterator will throw a * {@link ConcurrentModificationException}. Thus, in the face of * concurrent modification, the iterator fails quickly and cleanly, rather * than risking arbitrary, non-deterministic behavior at an undetermined * time in the future.
在这里就不贴代码了,直接上结果:
这就可以解释为什么for-each不能在内部使用集合的删除方法了——因为它底层调用了Iterator的next方法进行遍历。
3、为什么Iterator.remove()不会出现错位?
这也是Iterator设计的精髓所在。
private class Itr implements Iterator<E> { int cursor; // 即将取出的元素的角标 int lastRet = -1; // 上一次取出的元素的角标
int expectedModCount = modCount; // Iterator创建时的改动初始值 public boolean hasNext() { return cursor != size; //判断是否还有下一位元素存在,该方法要实现持续遍历判断,必须要配合next()方法 } @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; // 更新cursor的值为下一轮判定做准备 return (E) elementData[lastRet = i]; // 给lastRet赋值,否则remove()方法报错 } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; //对cursor进行修正 lastRet = -1; //remove()方法不能连续执行,因为lastRet不合法,只能先执行next() expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }
因为迭代器需要实现接口中操作对象的方法,而不同的对象其底层数据结构不同,因此迭代器的实现类需要根据对象的特点进行设计,也就是说迭代器只有在相应的对象中才有用,不如就在其内部定义得了。
4、总结
hasNext()的目的是判定循环是否超界,next()的目的是提供向下取值,通过cursor=cursor+1相联系,两者组合即替代了传统的for循环遍历。
next()和remove()也是按照顺序去执行的,通过lastRet=cursor、lastRet=-1和lastRet=i相联系的,两者组合确保了remove()方法顺利进行。 这三个方法就像齿轮一样,一环扣一环,非常精妙的设计。
因为modCount这个成员变量存放了信息,所以不是线程安全的。试想多个线程都用迭代器去操作同一个对象,modCount发生改变,其余线程的迭代操作也会报错。
标签:type 解释 就是 cep 情况 组合 安全 防止 自己实现
原文地址:https://www.cnblogs.com/mrpour/p/10764088.html