Java的for-each循环工作原理
Java的for-each循环工作原理
我正在处理某个任务时,偶然间按照我的理解做错了某件事情,但代码执行并得到了正确的结果。我有些惊讶,并且心里有个问题,就是这些for each循环是如何工作的。\n例如(示例程序),\n
public static void main( String[] args ) { String myInput = "hello , hi , how are you "; String[] splitted = myInput.split(","); Listmylist = new ArrayList (); for (String output : splitted) { mylist.add(output); } for (String output : mylist) { System.out.println(output); mylist = new ArrayList (); //它起作用了 mylist.add(output); } for (String output : splitted) { mylist.add(output); } for (String output : mylist) { System.out.println(output); mylist.add(output); //在这一行之后,它抛出了异常java.util.ConcurrentModificationException } }
\n我很好奇并且在搜索时找到了另一篇帖子,说我们可以使用迭代器方法从列表中删除元素,所以我尝试了一下,\n
for (String output : splitted) { mylist.add(output); } for (Iterator iterator2 = mylist.iterator(); iterator2.hasNext();) { String string = (String) iterator2.next(); System.out.println(string); iterator2.remove(); //它起作用了,但是如果我用同样的方法从原始列表中删除元素,它会抛出异常。 }
\n现在我只想知道在上述每个for each循环背后发生了什么。
\n我想了解技术方面的内容,我知道我不能在for each循环中修改集合,但在上述某些情况下它起作用了,为什么?
Java的for-each循环会被内部转换为带有迭代器的for循环。通过对给定的代码进行分析,我们可以看出在使用for-each循环遍历列表时,如果在迭代过程中对列表进行了修改,会导致ConcurrentModificationException异常的抛出。而使用迭代器的remove方法可以避免这种异常的发生。
首先,我们来看第一个示例代码。在这个例子中,使用for-each循环遍历了一个列表,并在循环内部对列表进行了修改。这种修改包括清空列表并重新向其中添加元素。由于for-each循环会被转换为带有迭代器的for循环,所以在转换后的代码中,会先获取到列表的迭代器,然后使用迭代器进行遍历。由于在循环开始之前就已经获取了列表的迭代器,所以循环会在列表的最后一个元素之前就结束。因此,这段代码是可以正常工作的。
接下来,我们看第二个示例代码。在这个例子中,同样使用for-each循环遍历了一个列表,并在循环内部对列表进行了修改。这次的修改是直接向列表中添加元素。由于使用的是for-each循环,而不是迭代器,所以在转换后的代码中并没有获取到列表的迭代器。因此,当在循环内部对列表进行修改时,就会抛出ConcurrentModificationException异常。
为了避免这种异常的发生,可以使用迭代器来进行列表的遍历和修改。在第三个示例代码中,使用迭代器的方式来遍历列表。在循环内部,使用迭代器的remove方法来删除元素。这种方式是可以正常工作的,因为在使用迭代器的remove方法删除元素时,迭代器会更新内部状态,以确保循环可以正常进行。但是需要注意的是,如果尝试使用相同的方式从原始列表中删除元素,仍然会抛出ConcurrentModificationException异常。
当使用for-each循环遍历列表时,在循环内部对列表进行修改会导致ConcurrentModificationException异常的抛出。为了避免这种异常,可以使用迭代器来进行列表的遍历和修改,同时使用迭代器的remove方法来删除元素。
Java for-each循环是一种可以应用于实现Iterable接口的类的循环方式。这意味着你可以自己创建类并在for-each循环中使用,这样非常方便。
实现Iterable接口需要你实现一个返回Iterator的iterator()方法。然后,for-each循环会获取该iterator并使用hasNext()和next()方法进行迭代。就像你自己手动实现迭代器一样。
使用for-each循环删除元素的问题在于,当你在列表中使用for-each循环删除元素时,构造的迭代器不会知道这个变化,从而导致ConcurrentModificationException异常。
但是,如果直接调用Iterator.remove()方法,迭代器将知道这个变化并进行处理。
为了避免迭代器和异常同时出现的常见小技巧是这样做的:
List
for (Object object : new ArrayList
objects.remove(object);
}
所以你创建了一个列表的临时副本,对其进行迭代,但是在原始列表上调用remove方法。
创建这个副本是愚蠢的。只需使用迭代器遍历ArrayList(因为这就是foreach循环所做的),就可以添加和删除项而无需任何错误和副本创建。如果确实需要并发操作,那么可以使用专门用于此目的的并发列表。
现在是2013年了。在99%的情况下,创建一个列表的副本都不是问题。我认为这种做法比使用迭代器更易读,在这些情况下我喜欢使用这个小技巧。如果你认为这很愚蠢,那么好吧,不要使用它。我认为这也是对问题的进一步解释...
这是一个完全无用的操作,而且还绕过了Java中的ConcurrentModificationException警告机制。因此,它不仅是不必要的,而且还隐藏了潜在的bug。如果有一个简单的替代方法,为什么要使用这样的东西呢?
这种做法会引入什么可能的bug呢?这是一个完全有效的解决方案,唯一的缺点是会创建一个副本。
Java中的for each循环的工作原理及解决方法
在Java中,for each循环可以用来遍历一个集合或数组。然而,在使用for each循环时,可能会遇到一些问题。本文将讨论for each循环出现的原因以及解决方法。
1. 遍历并添加元素到列表
当使用for each循环将一个数组的每个元素添加到一个列表中时,可以使用以下代码:
for (String output : splitted) { mylist.add(output); }
这段代码将splitted数组中的每个output字符串添加到mylist列表中。
2. 遍历并输出列表元素
当使用for each循环遍历一个列表并输出其中的元素时,可以使用以下代码:
for (String output : mylist) { System.out.println(output); mylist = new ArrayList(); mylist.add(output); }
这段代码等价于以下代码:
Iteratoriterator = mylist.iterator(); while (iterator.hasNext()) { System.out.println(output); mylist = new ArrayList (); mylist.add(output); }
这是因为for each循环的语法中,Expression必须是java.lang.Iterable的实例,或者是一个数组。在每次迭代时,mylist都会被重新赋值为一个新的ArrayList实例,但是迭代器仍然持有对原始mylist的引用,并继续遍历原始mylist中的元素。
3. 遍历并修改列表元素(抛出异常)
当使用for each循环遍历一个列表并在遍历过程中修改列表元素时,可能会抛出ConcurrentModificationException异常。以下代码展示了这种情况:
for (String output : mylist) { System.out.println(output); mylist.add(output); // 在这一行之后抛出异常java.util.ConcurrentModificationException }
这是因为ArrayList的迭代器是fail-fast的,即如果在迭代器创建后的任何时间内以除迭代器自己的remove或add方法之外的任何方式对列表进行了结构性修改,迭代器就会抛出ConcurrentModificationException异常。
4. 使用迭代器遍历并删除列表元素
当使用迭代器遍历一个列表并在遍历过程中使用迭代器的remove方法删除元素时,可以成功删除元素而不抛出异常。以下代码展示了这种情况:
for (Iteratoriterator2 = mylist.iterator(); iterator2.hasNext();) { String string = (String) iterator2.next(); System.out.println(string); iterator2.remove(); // 成功删除元素,但如果使用相同的方法删除原始列表中的元素,会抛出异常 }
这是因为迭代器的remove方法是安全的,可以在迭代过程中删除元素。然而,如果使用相同的方法删除原始列表中的元素,就会抛出ConcurrentModificationException异常。
需要注意的是,迭代器保持对创建它的列表的引用,因此对mylist重新赋值没有对迭代器所遍历的列表产生影响。
当使用for each循环遍历集合或数组时,需要注意在循环过程中对集合或数组进行结构性修改可能会导致异常的抛出。如果需要在遍历过程中对集合进行修改,可以使用迭代器的remove或add方法来安全地进行操作。