-
Iterator迭代器
属于设计模式,提供了一个方法,对集合/容器内的元素进行遍历,而不用关注底层实现细节,达到数据与上层遍历解耦的目的.(解耦: 访问逻辑从不同类型的集合类中抽取出来,接触两者间的联合关系。)
- Iterator迭代器提供的三种方法:(迭代器直接操作集合中的数据)
boolean hasNext();//判断集合中是否还有数据,有数据返回true;
E next();//获取当前遍历的元素
void remove();//删除元素
- Iterable接口(返回一个迭代器)
public interface Iterable<T> {//接口实现 返回一个迭代器
/**
* Returns an iterator over a set of elements of type T.
* @return an Iterator.
*/
Iterator<T> iterator();
}
- 自定义实现Iterator中的三种方法
public class MyArrayList<T> implements Iterable<T>{
private T[] arr;
private int size;
public MyArrayList(){
this(10);
}
public MyArrayList(int n){
size = 0;
arr = (T[])new Object[n];
}
public boolean isEmpty () {
return size == 0;
}
public boolean isFull () {
return size == arr.length;
}
public void grow () {
arr = Arrays.copyOf(arr, arr.length * 2);
}
public void add ( T data){
if (isFull()) {
grow();
}
arr[size++] = data;
}
public T get ( int index){
if (index < 0 || index > arr.length) {
return null;
}
return arr[index];
}
public int index(T data){
for(int i = 0;i < size ;i++){
if(arr[i] == data)
return i;
}
return -1;
}
public void remove ( T data){
if(isEmpty()){
return;
}
int index = index(data);
for(int i = index;i < size-1;i++){
arr[i] = arr[i+1];
}
arr[size--] = null;
}
public void show () {
for (T i : arr) {
System.out.println(i);
}
}
@Override
public Iterator<T> iterator() {
return new DIYItr();
}
class DIYItr implements Iterator<T>{
int before;
int after;
public DIYItr(){
before = after = 0;
}
@Override
public boolean hasNext() {
return after != size;
}
@Override
public T next() {
int i = after;
after = i+1;
before = i;
T result = arr[before];
return result;
}
@Override
public void remove() {
T r = arr[before];
MyArrayList.this.remove(r);
}
}
public static void main(String[] args){
MyArrayList<Integer> list = new MyArrayList<>();
list.add(3);
list.add(4);
list.add(6);
list.add(8);
list.add(9);
Iterator<Integer> itr = list.iterator();
while(itr.hasNext()){
Integer value = itr.next();
if(value == 6) {
itr.remove();
}
}
for(Integer i : list){
System.out.print(i+" ");
}
}
}
在程序运行中会发现:如果在迭代器进行遍历时,进行改变集合大小结构的操作(增加,删除等操作),会抛出ConcurrentModificationException(并发异常);
- 抛出并发性异常的原因是什么呢?
在迭代之前,迭代器已经被创建出来了,如果此时改变容器的大小结构,IIterator对象无法同步list,Java会认为这样的操作不安 全,就会进行提醒,抛出异常。从Iterator的实现源码可以看出:modCount是当前集合的版本号,每次add,remove,都会+1,expectedModCount是当前集合的版本号,迭代器初始化为modCount,而调用ArrayList.add(),或ArrayList.remove()时,只对modCount进行了操作,迭代器中的expectedModCount并未同步,导致在进行检查时,抛出异常。而使用Iterator.remove()就不会抛出异常:源码发现,该方法会将modCount与expectedModCount同步。
- 快速失败机制(fail-fast)和安全失败机制(fail-safe)
- 快速失败机制(fail-fast)
在迭代器进行遍历时,不能删除集合中的元素(改变集合大小结构的操作),否则会抛出ConcurrentModificationException
原理:使用迭代器进行集合遍历时,遍历过程有一个modCount变量,对集合进行remove,add等操作时,对modCount都会进行改变,每当使用hasNext()或next()遍历下一个元素时,都进行版本检查checkForComidification(),当modCount==expectedModCount时,继续遍历,否则,抛出异常,终止遍历。
- 安全失败机制:(fail-safe)
采用安全失败机制的容器,在遍历时,不是在原集合上直接操作,而是拷贝一份原集合,对拷贝的集合进行操作。
原理:由于操作的对象不是原集合,迭代器不能检测到,所以不会抛出并发异常。
缺点:由于遍历操作的是初始拷贝的集合,所以无法对修改后的集合进行操作/访问。
-
ListIterator提供双向遍历(有hasPrevious(),previous()),方法相对于Iterator迭代器提供了多个方法
-
Iterator和ListIterator的异同?(适用范围和继承结构)
异:适用范围:Iterator可以适用于所有集合类型,而ListIterator只能用于List及其子类型。
涵盖方法:ListIterator中有add(),可以添加元素,双向遍历,而Iterator不能添加元素,单向遍历。
同:二者都是迭代器,当需要对容器进行遍历且不需要干涉遍历过程,都可以用。