3

我正在执行某些任务,但偶然根据我的说法做错了,但代码执行并提供了正确的结果。我一点都不惊讶,并且想到每个循环的所有这些是如何工作的。示例(示例程序),

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.
}

现在我只想知道上面引用的每个循环后面发生了什么。
我想知道技术方​​面,我知道我不能修改每个循环中的集合,但在上面提到的某些情况下它起作用了,为什么?

4

5 回答 5

3

现在我只想知道上面引用的每个循环背后发生了什么

 1. for (String output : splitted) 
    {
        mylist.add(output);
    }

这会将数组中的每个output字符串添加到列表中。splittedmylist

2. for (String output : mylist) 
{
      System.out.println(output);
      mylist = new ArrayList<String>(); //It worked 
      mylist.add(output);
}

for语句受以下产生式支配:

for ( FormalParameter : Expression )
            Statement

whereExpression必须是 的实例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并将继续遍历 original 的元素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文档中复制的:

此类的 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 循环的行为:list在遍历列表时,可以通过迭代器自己的 remove 或 add 方法在结构上进行修改。

于 2013-10-22T09:14:58.897 回答
2

对于实现Iterable. 这也意味着您可以自己创建可以在 for-each 循环中使用的类,这会非常舒适。

这个接口强制你实现一个iterator()返回Iterator. 然后 for-each 循环什么都不做,只是检索该迭代器并使用hasNext()and对其进行迭代next()。就像你自己做的一样。

删除的问题是,当您使用 for-each 循环然后从 List 中删除一个元素时,构造函数Iterator将不知道有关该更改的任何内容,并且会有一个ConcurrentModificationException.

但是,如果您Iterator.remove()直接调用,迭代器将知道该更改并可以处理它。

同时避免迭代器和异常的一个常见小技巧是执行以下操作:

List<Object> objects = new ArrayList<Object>();
for (Object object : new ArrayList<Object>(objects)) {
    objects.remove(object);
}

因此,您创建了该列表的临时副本,对其进行迭代,但在原始列表上调用 remove。

于 2013-10-22T07:50:11.010 回答
2

List的 for-each 循环将在内部转换为带有迭代器的 for 循环

  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();) {

循环一直运行到列表的最后一个元素,即“你好吗”。

然而,由于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.
}
于 2013-10-22T09:08:03.853 回答
0

这是正确的。您不能使用“foreach”循环修改正在迭代的集合的值,为此,您必须使用集合的迭代器。

于 2013-10-22T07:50:26.730 回答
0

当然,将某些内容添加到与您当前正在遍历的列表完全不同的列表中当然不是问题,就像您对行所做的那样,mylist = new ArrayList<String>();即使变量仍然具有相同的名称,它将指向一个完全不同的列表。

您无法将某些内容添加到当前正在“遍历”的列表中的原因是,该列表的内部实现可能无法确保您仍然获得相同的元素顺序,尤其是不是所有剩余的元素你会期望的。如果您想象您正在使用排序列表,则可以最好地理解这一点:您放入了一个新元素,但您是否看到该元素未定义,因为这取决于您所在的位置和您插入的内容。由于 Java 不知道您是否对此感到满意,因此它会走安全的道路并抛出异常。

然而,有一些列表能够在遍历期间进行修改,主要是并发包中的并发列表

于 2013-10-22T08:38:23.170 回答