Java:如果使用for-each循环遍历集合,替代iterator.hasNext()的方法是什么?

16

我正在尝试使用for-each语句替换基于迭代器的Java列表循环,但代码在某些时候使用iterator.hasNext()检查是否到达列表中的最后一个元素。

for-each有类似的功能吗?

for (Object current : objectList) {
   if (last-element) 
      do-something-special
}

了解您为什么要用 for-each 语句替换基于迭代器的循环会很有帮助,特别是因为(正如大多数人指出的那样)您要替换的基于迭代器的方法提供了您所需的功能,而 for-each 语句则没有。 - delfuego
我想用for-each循环的优雅和简洁来替换它。但由于它无法覆盖现有的功能,我放弃了这个想法。 - Dan
http://download.oracle.com/javase/1.5.0/docs/guide/language/foreach.html - Bozho
可能是为特殊情况下的最后一个元素选择最佳循环习惯用语的重复问题。这个问题比较旧,但另一个问题有更多的赞同票数。 - Ciro Santilli OurBigBook.com
11个回答

21

for-each只是迭代器版本的语法糖,如果你检查已编译的字节码,你会注意到编译器实际上将其转换为迭代器版本。

使用for-each形式时,你无法检查是否还有更多的元素。如果需要该功能,请继续使用显式迭代器。


4
如果需要使用Iterator#remove()方法(如果实际的迭代器已经实现了该方法)…… - Andreas Dolk
如果您需要知道正在迭代的数组的当前索引。 - matt b
只有在处理列表或数组时,最后一部分才有意义;否则,您仍然需要创建和递增一个单独的i,因此最好继续使用foreach循环。 - Kevin Bourrillion

8
除了Luno的回答之外:
注:此处为HTML标签,无需翻译。
Iterator<MyClass> it = myCollection.iterator();
while(it.hasNext()) {
  MyClass myClass = it.next():
  // do something with myClass
}

翻译成:

for (MyClass myClass:myCollection) {
  // do something with myClass
}

4

正如其他人所说 - 这是不可能的。

只需要记住,foreach结构并不是万能的。它的引入是为了简化一种非常普遍的任务,即对集合中的每个元素执行相同的操作

在您的情况下,您不想对每个元素执行完全相同的操作 - 因此,foreach循环不是适合这项工作的正确工具。试图“黑客式”地将其强制执行这样做是愚蠢的,只需在经典for循环中使用显式迭代器即可。


2
foreach循环(或增强型for循环)没有跟踪当前正在迭代的元素的功能。没有办法找出正在处理哪个Collection的索引,或者在Iterable中是否有更多元素需要处理。
话虽如此,一个可行的解决方案是在foreach循环中保留对当前迭代对象的引用。
通过保留当前迭代操作的对象引用,一旦foreach循环结束,就能保留这个引用,在变量中剩下的就是最后一个元素。
这种解决方法只有在仅需要最后一个元素时才可行。
例如:
String lastString = null;

for (String s : new String[] {"a", "b", "c"}) {
    // Do something.

    // Keep the reference to the current object being iterated on.
    lastString = s;
}

System.out.println(lastString);

输出:

c

1

很遗憾,for each语法不允许您检查一个元素是否是列表中的第一个或最后一个。这是for each循环的已知限制。 我建议您继续使用迭代器。

如果您只需要检查第一个元素而不是最后一个元素,例如在进行字符串连接时,您可以改用以下代码:

boolean first = true;
for (Element e : list) {
  if (!first) {
    //do optional operation
  }
  //do other stuff
  first = false;
}

但我倾向于使用迭代器。


0

是的,你可以这样做,如果你不想使用显式迭代器语法,我会这样做:

for (Object current : objectList) {
   if (objectList.getLast().equals(current)) 
      do-something-special
}

这可能会产生误报,如果列表中的最后一个单词在列表中出现了多次,那么您的条件将针对每个出现而不仅仅是最后一次出现得到满足。 - Dingredient

0
除了bayer之外,你需要做一些不同的事情,因为没有方法getLast()。但是你可以使用这个objectList.size() - 1来代替它。
for (Object current : objectList) {
   if (objectList.get(objectList.size() - 1).equals(current)) 
      do-something-special
}

0

只需保存循环次数,示例:

    int loop = 0;

    for(Item item : items) {

        if(loop == 0) {
            //Is First Item
        }

        if(loop != items.size()-1) {
            //Has Next Item
        }

        if(loop == items.size()-1) {
            //Is Last Item
        }

        //Must Be Last Statement 
        loop++;
    }

这类似于 for(int i = 0; i < items.size(); i++) 循环;

items.size() 用于列表,而 items.length 用于数组;


0
如果你想使用for-each,可能会像这样:
if (objectList.indexOf(current)==objectList.size()-1) break;

对于大型列表来说,这可能会非常昂贵。 - Jorn
1
我们可以使用for-each循环遍历任何实现Iterable接口的类,它不必是列表,当然也不必提供任何indexOf方法。 - Mirek Pluta

0
int nElts = objectList.size();
int n = 0;
for (...) {
  if (++n == nElts) ...

这是我能想到的最好的。


3
在for循环中使用if !iter.hasNext()比直接使用显式迭代器更加冗长,不太容易理解,并且出现逻辑错误的概率更高。但是我认为是否更好还要根据具体情况而定。 - Andrzej Doyle
不,我同意这是一个可怕的解决方案。我会撤回它,但我会将它留在这里作为对其他人的警示。 - Carl Smotricz
2
这里的另一个问题是你假设你有一个集合(即你有一个size()方法),并且调用它不会很耗费时间。但是你可能有一个一般的Iterable对象,它没有提供size()方法。在这种情况下,迭代器是唯一的选择。 - Joachim Sauer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接