1. 概述

当我们遍历一个List时,for-each循环是一种优雅而简单的工具。有时,我们可能需要在迭代过程中执行特定操作或基于是否为最后一次迭代做出决策。

在这篇教程中,我们将讨论这种情况,并探索在for-each循环中判断当前迭代是否为最后一次的不同方法。

2. 问题介绍

首先,让我们创建一个电影标题的List作为示例:

List<String> MOVIES = List.of(
  "Titanic",
  "The Deer Hunter",
  "Lord of the Rings",
  "One Flew Over the Cuckoo's Nest",
  "No Country For Old Men");

如果我们只想在典型的for-each循环后获取最后一个元素,这并不困难。我们可以在每次迭代中简单地将变量重新赋值为当前元素。走完整个列表后,变量将保存最后一个元素:

String myLastMovie = "";
for (String movie : MOVIES) {
    // do some work with movie
    myLastMovie = movie;
}
assertEquals("No Country For Old Men", myLastMovie);

然而,有时候,我们需要在最后一次迭代时执行某些特定操作。因此,在循环过程中,我们必须在for-each循环内部确定最后一次迭代。

与传统的for循环不同,for-each循环隐藏了这种信息。因此,没有明确的迭代索引给这个问题带来了挑战。

接下来,我们将看看如何解决这个问题。

3. 使用IntStream

我们知道,使用传统for循环解决问题并不困难,因为我们知道当前迭代的索引:

int size = MOVIES.size();
String myLastMovie = null;
for (int i = 0; i < size; i++) {
    String movie = MOVIES.get(i)
    // ... work with movie
    if (i == size - 1) {
        myLastMovie = movie;
    }
}
assertEquals("No Country For Old Men", myLastMovie);

我们可以遵循同样的思路,**创建一个包含列表元素所有索引的IntStream**。然后,我们可以使用forEach()方法遍历索引,检查当前是否是最后一次迭代,就像在传统的for循环中一样:

int size = MOVIES.size();
Map<Integer, String> myLastMovie = new HashMap<>();
IntStream.range(0, size)
  .forEach(idx -> {
      String movie = MOVIES.get(idx);
      // ... work with movie
      if (idx == size - 1) {
          myLastMovie.put(idx, movie);
      }
  });
assertEquals(1, myLastMovie.size());
assertTrue(myLastMovie.containsKey(size - 1));
assertTrue(myLastMovie.containsValue("No Country For Old Men"));

如测试所示,我们使用了IntStream.range(0, list.size())方法初始化列表索引流。值得注意的是,我们使用了一个Map对象来存储最后的索引和值,以便稍后验证。这是因为在Lambda表达式中使用的局部变量必须是final或effectively final

4. 使用外部计数器

另一种选择是维护一个外部计数器变量来跟踪迭代次数。通过比较计数器与集合的大小,我们可以确定最后一次迭代:

int size = MOVIES.size();
String myLastMovie = null;
int cnt = 0;
for (String movie : MOVIES) {
    // ... work with movie
    if (++cnt == size) {
        myLastMovie = movie;
    }
}
assertEquals("No Country For Old Men", myLastMovie);

IntStream解决方案相比,这种方法更直接。

5. 使用Iterator进行遍历

尽管我们在IntStream和计数器方法中解决了问题,但两种解决方案都需要引入新变量并进行额外的比较以检测最后一次迭代。

接下来,我们将看看如何在使用Iterator遍历列表的同时确定最后一次迭代。

Iterator提供了**hasNext()方法,方便我们检查当前迭代是否为最后一次**。因此,我们不需要为此目的使用额外的变量:

String movie;
String myLastMovie = null;
for (Iterator<String> it = MOVIES.iterator(); it.hasNext(); ) {
    movie = it.next();
    // ... work with movie
    if (!it.hasNext()) { // the last element
        myLastMovie = movie;
    }
}
assertEquals("No Country For Old Men", myLastMovie);

如你所见,使用Iterator是解决这个问题的最佳选择

6. 总结

在这篇文章中,我们探讨了三种在使用for-each循环遍历List时识别最后一次迭代的方法。

由于其内置的hasNext()检查,Iterator方法可以作为解决此特定挑战的理想方案。

如往常一样,示例代码的完整源码可在GitHub上找到。