java-Iterator

3378 字
9 分钟

每日一言

如果什么都不做,那就什么也不会开始。

——《元气少女缘结神》

什么是Iterator

Iterator 迭代器是Java集合Collection框架中的一种机制,是一种用于遍历集合如(列表,集合,映射等实现Collection接口)的类。

原因是 Collection 接口继承了 java.lang.Iterable 接口。Iterable 接口定义了一个核心方法 iterator(),该方法返回一个 Iterator 对象。

因此,任何实现了 Collection 接口的类(如 ArrayList, LinkedList, HashSet, TreeSet 等)都必须提供 iterator() 方法的实现,从而允许你获取一个用于遍历其元素的 Iterator

使用Iteartor遍历集合

Iterator 类位于java.util包中,使用前需要引入:

import java.util.Ierator;

迭代器接口定义了几个方法:最常用的是以下三个:

  • next()-返回迭代器的下一个元素,并将迭代器的指针移到下一个位置
  • hashNext()-用于判断集合中是否还有下一个元素可以访问
  • remove()-从集合中删除迭代器最后访问的元素

获取Iterator

通过调用Iterator()方法获取Iterator对象:

// 引入 ArrayList 和 Iterator 类
import java.util.ArrayList;
import java.util.Iterator;

public class RunoobTest {
    public static void main(String[] args) {

        // 创建集合
        ArrayList<String> sites = new ArrayList<String>();
        sites.add("Google");
        sites.add("Runoob");
        sites.add("Taobao");
        sites.add("Zhihu");

        // 获取迭代器
        Iterator<String> it = sites.iterator();

        System.out.println(it);   // 输出迭代器对象
        //it.remove();    //抛出异常

        // 输出集合中的第一个元素
        System.out.println(it.next());
        it.remove();    // 删除第一个元素,无返回值
    }
}

输出为:

java.util.ArrayList$Itr@4617c264
Google

循环集合元素

让迭代器 it 逐个返回集合中所有元素最简单的方法是使用 while 循环:

while(it.hasNext()) {
    System.out.println(it.next());
}

For-each循环

Java 中的 for-each 循环(也称为增强型 for 循环)是一种语法糖(syntactic sugar),它简化了遍历数组或实现了 java.lang.Iterable 接口的集合(如 Collection)的过程。编译器会在编译时将其转换为等效的使用 Iterator 或索引访问的代码。

1. 遍历实现了Collection接口的对象

  • 当 for-each 循环用于遍历实现了 Iterable 接口的对象时(例如 ArrayList, HashSet 等),编译器会将其转换为使用 Iterator 的代码。

原始代码:

List<String> list = new ArrayList<>();
// ... 添加元素 ...
for (String element : list) {
    System.out.println(element);
}

编译后的代码:

List<String> list = new ArrayList<>();
// ... 添加元素 ...
Iterator<String> iterator = list.iterator(); // 1. 获取 Iterator
while (iterator.hasNext()) {             // 2. 使用 hasNext() 判断
    String element = iterator.next();      // 3. 使用 next() 获取元素
    System.out.println(element);         // 4. 执行循环体
}

我们看一个例子以便加深我们对于Iterator的理解:

// 引入 ArrayList 和 Iterator 类
import java.util.ArrayList;
import java.util.Iterator;

public class RunoobTest {
    public static void main(String[] args) {
        // 创建集合
        ArrayList<String> sites = new ArrayList<String>();
        sites.add("Google");
        sites.add("Runoob");
        sites.add("Taobao");
        sites.add("Zhihu");

        for(String site : sites) {
            if(site.equals("Runoob")) {
                site = "Runoob.com";
            }
        }

        for(String site : sites) {
            System.out.println(site);
        }
    }
}

输出:

Google
Runoob
Taobao
Zhihu

我们对于Runoob的修改并没有生效,这里我们解释一下原因:

  • Iterator类指向的为集合中元素的地址。
  • String类具有不可变性
  • Iterator.next()方法返回元素的引用。

1745830634662

由于String的不可变性,没有修改源集合中元素地址中的值,而是将引用指向了另一个地址:

1745830721544

如果我们将String修改为StringBuffer类:

// 引入 ArrayList 和 Iterator 类
import java.util.ArrayList;
import java.util.Iterator; // Iterator 未在此示例中使用,但保留 import

public class RunoobTest {
    public static void main(String[] args) {

        // 创建集合,使用 StringBuffer
        ArrayList<StringBuffer> sites = new ArrayList<StringBuffer>();
        sites.add(new StringBuffer("Google"));
        sites.add(new StringBuffer("Runoob"));
        sites.add(new StringBuffer("Taobao"));
        sites.add(new StringBuffer("Zhihu"));

        // 遍历并尝试修改 StringBuffer 对象的内容
        for(StringBuffer site : sites) {
            // 比较 StringBuffer 的内容
            if(site.toString().equals("Runoob")) {
                // 修改 StringBuffer 对象的内容
                site.delete(0, site.length()).append("Runoob.com");
            }
        }

        // 打印修改后的列表内容
        for(StringBuffer site : sites) {
            System.out.println(site); // 会自动调用 StringBuffer 的 toString()
        }
    }
}

我们则可以直接修改地址中的内容,从而达到修改集合中元素的效果,运行结果:

Google
Runoob.com
Taobao
Zhihu

2. 遍历数组

当 for-each 循环用于遍历数组时,编译器会将其转换为使用传统索引访问的 for 循环。

原始代码:

String[] array = {"a", "b", "c"};
for (String element : array) {
    System.out.println(element);
}

编译后的代码:

String[] array = {"a", "b", "c"};
for (int i = 0; i < array.length; i++) { // 1. 使用索引循环
    String element = array[i];          // 2. 通过索引访问元素
    System.out.println(element);      // 3. 执行循环体
}

遍历数组时哪怕直接使用索引来访问数组,但是在For-each的遍历中,仍然创建一个引用来访问变量。保证了使用for-each循环时的统一性。