Java 集合高级(二)单列集合三种通用遍历方式与迭代器底层源码

前言

上一篇我们讲解了集合整体架构、Collection 顶层接口基础 API,本篇学习所有单列集合通用的三种遍历方式:迭代器、增强 for 循环、forEach 方法,同时拆解迭代器底层源码,搞懂指针移动原理。

一、前置区分:哪些集合能用通用遍历

  1. List 接口:有索引,除通用三种遍历外,还支持普通 for 循环
  2. Set 接口:无索引,只能使用本篇三种通用遍历单列集合全部实现 Iterable 接口,所以都支持迭代器遍历。

二、方式 1:迭代器 Iterator 遍历

1. 迭代器核心两个方法

方法 作用
boolean hasNext() 判断集合是否还有未取出元素,有返回 true,无返回 false
E next() 取出当前指针指向的元素,同时指针自动后移一位

2. 基础代码示例

复制代码
import java.util.ArrayList;
import java.util.Iterator;

public class IteratorDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("拿");
        list.add("了");
        list.add("橘");
        list.add("子");
        list.add("跑");
        list.add("啊");

        // 1. 获取迭代器对象
        Iterator<String> it = list.iterator();
        // 2. while循环判断是否有下一个元素
        while(it.hasNext()){
            // 取出元素,指针后移
            String s = it.next();
            System.out.println(s);
        }
    }
}

重要注意点

循环中next()方法只调用一次,多次调用会出现元素跳跃、NoSuchElementException 异常。

3. ArrayList 迭代器底层源码解析

复制代码
private class Itr implements Iterator<E> {
    int cursor;       // 指针,记录当前待取元素下标
    int lastRet = -1;

    // 判断是否还有元素
    public boolean hasNext() {
        return cursor != size;
    }

    // 取出元素,指针+1
    public E next() {
        int i = cursor;
        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }
}

运行逻辑:

  1. cursor 初始值 0,size 为集合总元素数量;
  2. hasNext()判断 cursor 是否等于 size,不等代表还有元素;
  3. next()先保存当前 cursor 下标,cursor 自增 1,再取出数组对应下标元素返回;
  4. 当 cursor 等于 size 时,hasNext()返回 false,循环结束。

迭代器小结

复制代码
Iterator<String> it = 集合对象.iterator();
while(it.hasNext()) {
    String s = it.next();
    System.out.println(s);
}
  • hasNext():判断剩余元素
  • next():取元素 + 指针后移
  • 禁止循环内多次调用 next ()

三、方式 2:增强 for 循环(JDK5 推出)

1. 底层本质

内部依旧封装 Iterator 迭代器,简化迭代器代码书写,仅用于遍历集合 / 数组,无法在遍历过程中修改集合

2. 语法格式

复制代码
for (元素数据类型 变量名 : 集合/数组) {
    // 使用变量
}

3. 代码演示

复制代码
import java.util.ArrayList;

public class ForEachDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");

        // 增强for遍历
        for (String s : list) {
            System.out.println(s);
        }
    }
}

四、方式 3:forEach 遍历方法(JDK8)

1. 方法定义

Collection 接口默认方法:default void forEach(Consumer<? super T> action)接收函数式接口 Consumer,遍历每个元素执行自定义操作。

2. Lambda 写法

复制代码
import java.util.ArrayList;

public class ForEachMethodDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("苹果");
        list.add("香蕉");
        list.add("葡萄");
        // lambda表达式
        list.forEach(s -> System.out.println(s));
    }
}

五、本篇统一小结

  1. 单列集合通用三种遍历:迭代器 Iterator、增强 for 循环、forEach 方法;
  2. Set 无索引,只能用这三种;List 额外支持普通 for、ListIterator;
  3. 迭代器依靠 cursor 指针遍历,hasNext 判断、next 取值并移动指针;
  4. 增强 for 底层是迭代器,语法最简,仅做遍历;
  5. forEach 是 JDK8 函数式遍历,配合 Lambda 简化代码;
  6. 遍历集合时,不要多次调用 next (),避免异常。