关于java:为什么通过ArrayList循环与常规数组相同?

Why Looping through ArrayList is same as a regular array?

本问题已经有最佳答案,请猛点这里访问。

方法1

1
2
3
4
5
6
7
ArrayList<String> list = new ArrayList<String>();

for (String s : list) {
    write.append(s);
    write.append('
'
);
}

如何以这种方式循环ArrayList? 这种方法不适用于静态常规数组,例如:String[] list2 = new String[]{"1","2","3","4"};

如果我想遍历ArrayList list,为什么不这样做:
方法2

1
2
3
4
5
for (int i = 0; i < list.size(); i++) {
    write.append(list.get(i));
    write.append("
"
);
}

我只是不明白如何使用方法1.欢迎任何解释。


这些被称为每个循环,并且可以应用于数组和集合。格式:

1
2
3
for ([type] [var-name] : [array or iterable value]) {
    ...
}

要使用for-each循环,您只需要满足以下两个条件之一:

  • 迭代值是一个数组,或
  • 迭代值是Iterable
  • 我们这样做的最大原因是因为List不是我们可以在循环中使用的唯一Iterable,但它是唯一可以被索引的。这允许您进行通用迭代,而不是将自己锁定为固定的索引列表格式。

    例如,HashSet不是有序的,但我仍然可以在for-each循环中迭代它:

    1
    2
    HashSet<String> strs = new HashSet<String>();
    for (String str : strs) { ... }

    我也可以使用Iterable而不是List的方法:

    1
    2
    3
    4
    5
    public static void printStrings(Iterable<String> strs) {
        for (String str : strs) {
            System.out.println(str);
        }
    }

    我没有办法使用索引迭代它而不先将其复制到其他东西,如数组或List

    使用for-each循环的另一个重要原因是它编译为使用列表中的Iterator(通过Iterable.iterator()),这允许快速失败迭代。这导致像ConcurrentModificationException这样的东西告诉我们在我们迭代它时集合被修改了。例如,这将始终抛出ConcurrentModificationException

    1
    2
    3
    4
    List<String> strs = ... ;
    for (String str : strs) {
        strs.remove(str);
    }

    但这不会:

    1
    2
    3
    4
    List<String> strs = ... ;
    for (int i = 0; i < strs.length(); i++) {
        strs.remove(str);
    }

    这对多线程应用程序很有用,因为你应该在访问它之前锁定列表,因为大多数List实现不是为了线程安全而设计的,并且迭代在没有外部同步的情况下绝不是线程安全的,两者都带有索引和Iterator。如果一个线程正在迭代它而另一个线程对其进行更改,则可以(不保证,但可以)抛出ConcurrentModificationException,这会告诉您列表未正确锁定。索引的索引永远不会给你这种信息。相反,它只会表现出像跳过元素这样的奇怪行为。


    ArrayList实现Iterable,使其提供Iterator对象,然后用户通过for循环。

    任何实现Iterable的类都可以在for循环中使用,就像数组一样。

    对于数组和集合,简化的for循环看起来是相同的,但在引擎盖下,它使用数组的索引和集合的迭代器。


    有一个名为Iterable的接口允许快速迭代。如果某些东西实现Iterable,则允许快速迭代。 Iterable接口有一个方法,它返回一个Iterator,可用于迭代元素。


    第一个被称为增强型,或"为每个"。它被认为是for语句的传统模型的语法糖 - 允许您迭代集合中的每个元素 - 无论是数组还是Java Collections Framework中的任何元素。

    这些类型的循环之间存在两个明显的差异:

    • 增强的for语句为您索引值,将其作为声明的一部分存储在临时变量中。因此,您不必执行val[i]value.get(i)之类的操作 - 这会自动发生。
    • 您无法在增强的for语句中指定增量;您要么迭代集合中的所有元素,要么通过break语句提前终止。

    可以使用标准数组迭代的原因在JLS中定义,特别是§14.14.2 - 因为数组没有实现Iterable,所以它将它转换为标准行为的for语句。

    • ...Otherwise, the Expression necessarily has an array type, T[].

      Let L1 ... Lm be the (possibly empty) sequence of labels immediately preceding the enhanced for statement.

    The enhanced for statement is equivalent to a basic for statement of the form:

    1
    2
    3
    4
    5
    6
    T[] #a = Expression;
    L1: L2: ... Lm:
    for (int #i = 0; #i < #a.length; #i++) {
        VariableModifiers(opt) TargetType Identifier = #a[#i];
        Statement
    }

    #a and #i are automatically generated identifiers that are distinct from any other identifiers (automatically generated or otherwise) that are in scope at the point where the enhanced for statement occurs.


    这是for-each语法:http://docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html

    编译器会将方法1中的代码转换为:

    1
    2
    3
    4
    5
    6
    for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
      String s = iterator.next();
      write.append(s);
      write.append('
    '
    );
    }