浅析Java中的Iterator

112 阅读3分钟

文章目录


1. 概念

Iterator对象也被称为迭代器,它主要用于遍历Collection集合中的的元素。

迭代器模式:给迭代器 模式的定义为:提供一种方法访问个容(container) (container) (container) 对象中各个元 素,而又不需暴露该对象的内部细节。


2. 常用方法

java.util.Iterator接口中常用的方法:

  • boolean hasNext():判断集合中是否还有没有下一个元素,有则返回true,否则返回false
  • E next():返回迭代的下一个元素

Iterator是一个接口,如果要使用它需使用Iterator接口的实现类对象。Collection继承了Iterator接口,因此,Colleantion接口的实现类对象可以使用iterator()方法用于返回迭代器的实现类对象

  • Iterator<E> iterator():返回在此collection的元素上进行迭代的迭代器

使用步骤:

  1. 使用集合中的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收
  2. 使用Iterator接口中的方法hasNext()判断还有没有下一个元素
  3. 使用Iterator接口中的方法next()取出集合中的下一个元素
public class IteratorDemo {

    @Test
    public void testList() {
        Collection<String> col = new ArrayList<>();

        List<String> list = new ArrayList<>();
        Collections.addAll(list, "Forlogen", "Kobe", "James");
        col.addAll(list);
        System.out.println(Arrays.toString(col.toArray()));
        System.out.println("-----------");

        //Iterator<E>接口也是泛型的,迭代器的泛型跟着集合走
        Iterator<String> iter = col.iterator();
        while (iter.hasNext()) {
            System.out.println(iter.next()); // Forlogen  kobe
        }
        System.out.println("-----------");

        // for-each遍历
        for (String s : col) {
            System.out.println(s);
        }
        System.out.println("-----------");

        // Lambda表达式遍历
        col.forEach((t) -> System.out.println(t));
        System.out.println("-----------");
    }
    /*
    [Forlogen, Kobe, James]
    -----------
    Forlogen
    Kobe
    James
    -----------
    Forlogen
    Kobe
    James
    -----------
    Forlogen
    Kobe
    James
    -----------
     */
    @Test
    public void testSet(){
        Set<String> set = new HashSet<>();
        Collections.addAll(set,"Forlogen", "Kobe", "James","James");

        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

    /*
    Forlogen
    Kobe
    James
     */
}

3. 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());
    }
}

Iterator接口的源码中主要是hasNext()next()两个方法,任何实现这个接口的类都要重写这两个方法。下面我们看一下ArrayList中对于这两个方法是如何实现的:

    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        Itr() {}

        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];
        }

        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();
            }
        }

        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            Objects.requireNonNull(consumer);
            final int size = ArrayList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }
		
        // 判断集合的修改次数是否合法
        // modCount用于记录集合被修改的次数,每当集合内部结构发生变化(add,remove,set)时,modCount+1
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

ArrayList内部有一个Iterator接口的实现类ItrItr中定义了三个变量:

  • cursor:表示下一个元素的索引位置
  • lastRet:表示上一个元素的索引位置
  • expectModCount:预期被修改的次数

对于接口中方法的实现:

  • hasNext():在Iterator接口的实现类中首先设置了一个游标cursor,它用于表示当前位置是否到达了集合的最后一个元素

    • 如果 cursor != size,说明集合后面还有元素,返回true
    • 如果cursor == size,说明已到达集合的末尾,返回false
  • 如果符合正常情况,则根据游标来获取当前位置的元素,并更新游标


4. for - each循环

借助于Iterator接口的实现。遍历操作还可以使用for-each来遍历数组和集合:

  • Collection<E> extends Iterable<E>:所有的打猎集合都可以使用增强for
  • public interface Iterable<T>:实现这个接口允许对象称为foreach语句的目标

格式:

for(集合/数组的数据里欸选哪个 变量名:集合名/数组名){
	sout(变量名)
}

测试代码:

@Test
 public void testForEach(){
 	//遍历集合
     List<String> list = new ArrayList<>();
     Collections.addAll(list, "Forlogen", "Kobe", "James");

     for(String ele:list){
         System.out.println(ele);
     }
     System.out.println("-----------");

	// 遍历数组
     String[] arr = {"James", "kobe"};
     for(String ele:arr){
         System.out.println(ele);
     }
 }
 /*
 Forlogen
 Kobe
 James
 -----------
 James
 kobe
  */