我正在处理某项任务,根据我的意外做错了,但代码已执行并提供了正确的结果。我有点惊讶,并且记住了每个循环的所有这些是如何工作的。 示例(示例程序),
public static void main( String[] args )
{
String myInput = "hello , hi , how are you ";
String[] splitted = myInput.split(",");
List<String> mylist = new ArrayList<String>();
for (String output : splitted)
{
mylist.add(output);
}
for (String output : mylist)
{
System.out.println(output);
mylist = new ArrayList<String>(); //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.
}
现在我只想知道上面引用的每个循环背后发生的事情 我想知道技术方面,我知道我不能修改每个循环的集合,但在某些情况下上面说明它有用吗?
答案 0 :(得分:3)
现在我只想知道每个人背后发生了什么 上面引用的循环
1. for (String output : splitted)
{
mylist.add(output);
}
这会将output
数组中的每个splitted
字符串添加到mylist
列表中。
2. for (String output : mylist)
{
System.out.println(output);
mylist = new ArrayList<String>(); //It worked
mylist.add(output);
}
for
语句由以下产品管理:
for ( FormalParameter : Expression )
Statement
其中Expression
必须是java.lang.Iterable
的实例或数组。所以这个for:each
循环等同于:
Iterator<String> iterator = mylist.iterator();
while (iterator.hasNext()) {
System.out.println(output);
mylist = new ArrayList<String>(); //It worked
mylist.add(output);
}
此处mylist.iterator()
将返回Iterator
类型的新实例:
public Iterator<E> iterator() {
return new Itr();
}
因此,即使您在每次迭代时创建新的ArrayList
实例并将它们分配给mylist
,从原始mylist
获取的迭代器仍将具有对原始mylist
的引用。 1}}并将继续遍历原始mylist
的元素。迭代器保持对其创建的列表的引用。赋值mylist = new ArrayList<String>()
对迭代器处理的数据没有影响,因为它更改了变量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:
此类的迭代器和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方法可以对list
进行结构修改。
答案 1 :(得分:2)
对于实现Iterable
的类,可以使用for-each循环。这也意味着您可以自己创建类,您可以在每个循环中使用它们,这非常舒适。
此界面强制您实现返回iterator()
的方法Iterator
。然后for-each循环什么都不做,只是检索迭代器并使用hasNext()
和next()
迭代它。就像你自己做的一样。
删除的问题是当你使用for-each循环然后从List中删除一个元素时,构造的Iterator
将不知道有关该更改的任何内容,并且会有一个ConcurrentModificationException
但如果您直接致电Iterator.remove()
,Iterator会知道该更改并可以处理。
同时避免迭代器和异常的一个常见小技巧是做这样的事情:
List<Object> objects = new ArrayList<Object>();
for (Object object : new ArrayList<Object>(objects)) {
objects.remove(object);
}
因此,您创建该List的临时副本,迭代它,但在原始List上调用remove。
答案 2 :(得分:2)
for-each循环将在内部转换为 for for iterator 。
for (String output : mylist)
{
System.out.println(output);
mylist = new ArrayList<String>(); //It worked
mylist.add(output);
}
转换为
for (Iterator<String> iterator = mylist.iterator(); iterator.hasNext();) {
String output = (String)iterator.next();
System.out.println(output);
mylist = new ArrayList<String>(); //It worked
mylist.add(output);
}
由于列表的快照已经在下面进行了
for (Iterator<String> iterator = mylist.iterator(); iterator.hasNext();) {
循环一直运行到列表的最后一个元素,即“你好吗”。
然而,由于列表的FailFast behaviour,以下内容无效。
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.
}
答案 3 :(得分:0)
这是正确的。您不能使用“foreach”循环修改正在迭代的集合的值,为此,您必须使用集合的迭代器。
答案 4 :(得分:0)
将某些内容添加到与您当前遍历的列表完全不同的列表当然不是问题,就像您对行mylist = new ArrayList<String>();
所做的那样。即使变量仍具有相同的名称,它也会指向到一个完全不同的列表。
您无法向当前“走过”的列表中添加内容的原因是,该列表的内部实现可能无法确保您仍然获得相同的元素顺序,尤其不是全部剩下的元素就像你期望的那样。 如果你想象你正在使用一个排序列表,这是最好的理解:你输入一个新元素,但是你是否看到该元素是未定义的,因为它取决于你的位置和插入的内容。由于Java不知道你是否对它好,它需要安全的道路并抛出异常。
但是有些列表能够在遍历期间进行修改,主要是concurrent package中的并发列表。