java中的Iterator迭代器及源码分析

迭代器接口:

public interface Iterator<E> {
	boolean hasNext();
	E next();
	default void remove() {
        throw new UnsupportedOperationException("remove");
    }
	default void forEachRemaining(Consumer<? super E> action) {
	      Objects.requireNonNull(action);
	      while (hasNext())
	          action.accept(next());
    }

使用方法:

		ArrayList list=new ArrayList<String>();
        list.add("a");
        list.add("b");
        list.add("c");
        Iterator iterator = list.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }

迭代器通常被称为轻量级对象(lightweight object):创建它的代价小。因此,经常可以看到一些对迭代器有些奇怪的约束。例如,Java 的 Iterator 只能单向移动。–摘自《java编程思想》
我们拿ArrayList举例,它实现了Collection接口中的Iterator iterator();方法
实现:

  public Iterator<E> iterator() {
        return new Itr();
    }

new Itr()是ArrayList里面的内部类,该内部类实现了Iterator接口,重写了上面写的Iterator中的四个方法
事实上还有一个方法

     final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

这个方法在next()方法一开始就被调用:

public E next() {
   checkForComodification();
     ......
}

expectedModCount是预计修改次数,在我们一开始创建iterator的时候就会赋值,后面就不会改变
modCount 是修改次数
ArrayList中,当我们添加元素的时候,modCount++,这时候modCount != expectedModCount,checkForComodification抛出异常,这就是为什么,迭代器遍历的时候不能添加元素的原因。如果想添加元素,可以使用ListIterator(下面会提到)

iterator.next()方法可以理解为从起始位置 越过 第一个元素 来到到 第一个元素与第二个元素之间的位置,返回刚刚越过的元素的引用也就是AAA
在这里插入图片描述
remove方法会删除上次调用next方法时返回的元素,所以如果要想删除一个元素实现要越过它。
如果我们要删除BBB和CCC我们需要

Iterator iterator = list.iterator();
iterator.next();
iterator.next();//来到BBB和CCC中间
iterator.remove();//删除BBB
iterator.next();//来到CCC右边
iterator.remove();//删除CCC

使用iterator不需要知道它所遍历的序列的类型信息

ListIterator

有一点需要注意:ArrayList,LinkedList是有序集合,迭代器从头遍历到尾,这也就意味着,像add这种依赖位置的方法可以实现,并且依赖于迭代器来实现。而像Set,Map这样的无序集合就不能add,只能使用Iterator,为了让list实现迭代器的add,我们有提供了ListIterator。

ListIterator的add方法添加的元素位置在迭代器前面
ListIterator还可以从尾往头遍历

boolean hasPrevious();
E previous();

当从尾往头遍历时,remove()删除的是右边的元素(也是刚刚越过的元素)

add方法只依赖迭代器的位置,而remove方法不同,它依赖迭代器的状态
所以add方法可以连续调用多次,而remove方法不可以连续调用两次

另外ListIterator还有一个方法:

void set(E e);

是用一个新元素替换调用next或preivous方法返回的上一个元素

迭代器模式(23种设计模式之一):

  1. 迭代器模式属于行为模式
  2. 迭代器遍历这种方式不会暴露元素内部结构(比如有数组,有java集合类,或者其他什么方式),不需要知道你的类型也可以遍历
  3. 迭代器模式,提供统一遍历集合元素的接口,用一致的方式遍历集合元素

List中的迭代器

    public Iterator<E> iterator() {
        return new Itr();
    }

返回的是一个Itr内部类,它实现了Iterator接口

   private class Itr implements Iterator<E> {
        int cursor;       // 要返回的下一个元素的索引
        int lastRet = -1; // 返回的最后一个元素的索引; 如果最后一个元素被删除了就返回-1(在remove中会被赋值为-1)
        int expectedModCount = modCount;  //判断是否更改list结构(通过list.add()添加,list.remove()删除,注意:list里面的修改set(index,value)不改变表结构)

        Itr() {}
        
        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;
            //把当前元素(也就是刚刚越过去的元素)的位置赋值给lastRet
            return (E) elementData[lastRet = i];
        }

		public void remove() {
			//确保删除前已经next()了
            if (lastRet < 0)
                throw new IllegalStateException();
                //迭代器remove前会做是否更改list结构的判断
            checkForComodification();

            try {
            	//迭代器中的remove也是使用list.remove()
                ArrayList.this.remove(lastRet); //lastRet刚刚越过去元素的下标
                cursor = lastRet;
				//删除后就被赋值为-1,上面是if (lastRet < 0)
				//这就是为什么迭代器不能连续删除元素的原因
				//lastRet 也可以理解为迭代器的状态,-1为删除元素后的状态
                lastRet = -1;	
                //这段代码改变了expectedModCount
                //只许州官放火,不许百姓点灯🤷‍♂️
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
		......
}

HashMap中的迭代器

要使用HashMap中的迭代器,有三种形式,或者说,我们要把hashmap先转化一下,然后再调用iterator()方法,有三种形式:1.KeySet;2.Values;3.EntrySet(是前面两种的合体)。
这是三个内部类,三个类中都有iterator()方法

这三个方法有很多相似性

KeySet的:

public final Iterator<K> iterator()     { return new KeyIterator(); }

Values的:

public final Iterator<V> iterator()     { return new ValueIterator(); }

EntrySet的:

public final Iterator<Map.Entry<K,V>> iterator() {
    return new EntryIterator();
}

这三个方法new的内部类在源码中都写在了一起整整齐齐😂(它们都继承了HashIterator)

    final class KeyIterator extends HashIterator
        implements Iterator<K> {
        public final K next() { return nextNode().key; }
    }

    final class ValueIterator extends HashIterator
        implements Iterator<V> {
        public final V next() { return nextNode().value; }
    }

    final class EntryIterator extends HashIterator
        implements Iterator<Map.Entry<K,V>> {
        public final Map.Entry<K,V> next() { return nextNode(); }
    }

nextNode()方法就是HashIterator中的,我们就只看第三个EntryIterator 的nextNode(),前面两一样

下面是HashIterator的源码:

   abstract class HashIterator {
        Node<K,V> next;        // 下一个节点
        Node<K,V> current;     // 当前节点
        int expectedModCount;  // 为防止迭代器遍历时期 修改结构所做的标记(map.put添加,其实这里还包括修改,map.remove删除)
        int index;             // current slot

        HashIterator() {
            expectedModCount = modCount;
            Node<K,V>[] t = table;
            current = next = null;
            index = 0;
            //构造的时候就找到第一个元素
            if (t != null && size > 0) { // advance to first entry
                do {} while (index < t.length && (next = t[index++]) == null);
            }
        }

        public final boolean hasNext() {
            return next != null;
        }

        final Node<K,V> nextNode() {
            Node<K,V>[] t;
            Node<K,V> e = next;
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if (e == null)
                throw new NoSuchElementException();
                //如果链表中的当前节点还有next就直接返回,当前节点e
            if ((next = (current = e).next) == null && (t = table) != null) {
                //如果当前节点没有next,也就是说链表走到头了,进入do-while循环,进入下一个不为null的桶
                do {} while (index < t.length && (next = t[index++]) == null);
            }
            return e;
        }

        public final void remove() {
            Node<K,V> p = current;
            if (p == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            current = null;
            K key = p.key;
            removeNode(hash(key), key, null, false, false);
            expectedModCount = modCount;
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值