每个循环工作的Java
我正在处理某项任务, 当我偶然做错了什么但是代码执行并提供了正确的结果。 我对此感到有些惊讶,并且对于每个循环的所有这些是如何工作都有疑问。 示例(示例程序),
public static void main( String[] args ) { String myInput = "hello , hi , how are you "; String[] splitted = myInput.split(","); List mylist = new ArrayList(); for (String output : splitted) { mylist.add(output); } for (String output : mylist) { System.out.println(output); mylist = new ArrayList(); //It worked mylist.add(output); } for (String output : splitted) { mylist.add(output); } for (String output : mylist) { System.out.println(output); mylist.add(output); // After this line it threw exception java.util.ConcurrentModificationException } }
我很想知道,在搜索的过程中,我发现另外一篇文章说如果我们使用迭代器方法我们可以从列表中删除元素,所以我试过,
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(); //It worked but if I used the same thing to remove element from original list it threw exception. }
现在我只想知道上面引用的每个循环背后发生的事情。
我想知道技术方面,我知道我无法修改每个循环的集合,但在某些情况下,上面说明了它有用吗?
现在我只想知道上面引用的每个循环背后发生的事情
1. for (String output : splitted) { mylist.add(output); }
这会将splitted
数组中的每个output
字符串添加到mylist
列表中。
2. for (String output : mylist) { System.out.println(output); mylist = new ArrayList(); //It worked mylist.add(output); }
for
语句由以下产品控制:
for ( FormalParameter : Expression ) Statement
其中Expression
必须是java.lang.Iterable
或数组的实例。 所以这个for:each
循环都等同于:
Iterator iterator = mylist.iterator(); while (iterator.hasNext()) { System.out.println(output); mylist = new ArrayList (); //It worked mylist.add(output); }
这里mylist.iterator()
将返回Iterator
类型的新实例:
public Iterator iterator() { return new Itr(); }
因此,即使您正在创建新的ArrayList
实例并在每次迭代时将它们分配给mylist
,从原始mylist
获取的迭代器仍将具有对原始mylist
的引用,并将继续迭代原始mylist
的元素。 迭代器保留对其创建的列表的引用。 赋值mylist = new ArrayList
对迭代器处理的数据没有影响,因为它更改了变量mylist
而不是list
本身。
3. for (String output : mylist) { System.out.println(output); mylist.add(output); // After this line it threw exception java.util.ConcurrentModificationException }
以下语句解释了这种行为。 它是从Arraylist
doc复制的:
这个类的iterator和listIterator方法返回的迭代器是快速失败的:如果在创建迭代器之后的任何时候对列表进行结构修改,除了通过迭代器自己的remove或add方法之外,迭代器将抛出ConcurrentModificationException。 因此,在并发修改的情况下,迭代器快速而干净地失败,而不是在未来的未确定时间冒任意,非确定性行为的风险。
4. for (Iterator iterator2 = mylist.iterator(); iterator2.hasNext();) { String string = (String) iterator2.next(); System.out.println(string); iterator2.remove(); //It worked but if I used the same thing to remove element from original list it threw exception. }
上面的语句还解释了这个for循环的行为:迭代遍历列表时,迭代器自己的remove或add方法可以在结构上修改列表。
对于实现Iterable
类,可以使用for-each循环。 这也意味着您可以自己创建类,您可以在每个循环中使用它,这可能非常舒适。
此接口强制您实现返回Iterator
的方法iterator()
。 然后for-each循环什么都不做,只是检索迭代器并使用hasNext()
和next()
迭代它。 就像你自己做的一样。
删除的问题是当你使用for-each循环然后从List中删除一个元素时,构造的Iterator
将不知道有关该更改的任何信息,并且会出现ConcurrentModificationException
。
但是,如果直接调用Iterator.remove()
,Iterator将知道该更改并可以处理它。
同时避免迭代器和exception的常见小技巧是执行以下操作:
List
因此,您创建该List的临时副本,迭代它,但在原始List上调用remove。
for-each的每个循环都将在内部转换为for循环与迭代器 。
for (String output : mylist) { System.out.println(output); mylist = new ArrayList(); //It worked mylist.add(output); }
转换为
for (Iterator iterator = mylist.iterator(); iterator.hasNext();) { String output = (String)iterator.next(); System.out.println(output); mylist = new ArrayList (); //It worked mylist.add(output); }
由于列表的快照已经在下面进行了
for (Iterator iterator = mylist.iterator(); iterator.hasNext();) {
循环运行直到列表的最后一个元素,即“你好吗”。
然而,由于List的FailFast行为 ,以下不起作用。
for (String output : mylist) { System.out.println(output); mylist.add(output); // After this line it threw exception java.util.ConcurrentModificationException }
它说,如果你在迭代时修改列表,除了迭代器自己的remove方法之外的任何东西,List将抛出ConcurrentModificationException,这就是下面工作的原因。
for (Iterator iterator2 = mylist.iterator(); iterator2.hasNext();) { String string = (String) iterator2.next(); System.out.println(string); iterator2.remove(); //It worked but if I used the same thing to remove element from original list it threw exception. }
这是正确的。 你不能修改使用“foreach”循环迭代的集合的值,为此,你必须使用集合的迭代器。
将一些内容添加到与当前遍历的列表完全不同的列表当然不是问题,就像使用行mylist = new ArrayList
尽管变量仍具有相同的名称,但它将指向完全不同的列表。
您无法向当前正在“遍历”的列表添加内容的原因是,该列表的内部实现可能无法确保您仍然获得相同的元素顺序,尤其是不是所有剩余元素你会期待的。 如果你想象你正在使用一个排序列表,这可以理解得最好:你输入一个新元素,但是你是否看到该元素是未定义的,因为它取决于你的位置和你插入的内容。 由于Java不知道你是否对它好,它需要安全的道路并抛出exception。
但是,有些列表能够在遍历期间进行修改,主要是并发包中的并发列表。