为啥循环遍历 ArrayList<String> 与常规数组相同? [复制]

Posted

技术标签:

【中文标题】为啥循环遍历 ArrayList<String> 与常规数组相同? [复制]【英文标题】:Why Looping through ArrayList<String> is same as a regular array? [duplicate]为什么循环遍历 ArrayList<String> 与常规数组相同? [复制] 【发布时间】:2013-07-20 02:40:27 【问题描述】:

方法一

ArrayList<String> list = new ArrayList<String>();

for (String s : list) 
    write.append(s);
    write.append('\n');

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

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

for (int i = 0; i < list.size(); i++) 
    write.append(list.get(i));
    write.append("\n");

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

【问题讨论】:

搜索“enhanced for loop”和“iterables”并阅读它们! 不,这不是重复。我想知道原因,没有解释为什么以及如何可能。 我意识到 - 这里是a better resource。 @WilliamFox 我没有投票赞成关闭,只是输入了我的建议。 same.. 没有解释如何使用带有ArrayList 的方法 1 而不是Array 来进行迭代。当我说数组时,我的意思是String[] bla = new String[] 【参考方案1】:

ArrayList 实现了Iterable,这使得它提供了一个Iterator 对象,然后由 for 循环用户使用。

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

简化的 for 循环对于数组和集合看起来是一样的,但在底层,它使用数组的索引和集合的迭代器。

【讨论】:

【参考方案2】:

这些称为 for-each 循环,可以应用于数组和集合。格式:

for ([type] [var-name] : [array or iterable value]) 
    ...

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

    迭代值是一个数组,或者 迭代值为Iterable

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

例如,HashSet 没有排序,但我仍然可以在 for-each 循环中对其进行迭代:

HashSet<String> strs = new HashSet<String>();
for (String str : strs)  ... 

我还可以有一个方法采用Iterable 而不是List

public static void printStrings(Iterable<String> strs) 
    for (String str : strs) 
        System.out.println(str);
    

如果不先将其复制到其他东西(例如数组或List)中,我无法使用索引对其进行迭代。

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

List<String> strs = ... ;
for (String str : strs) 
    strs.remove(str);

但这不会:

List<String> strs = ... ;
for (int i = 0; i < strs.length(); i++) 
    strs.remove(str);

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

【讨论】:

【参考方案3】:

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

【讨论】:

“快速”迭代是什么意思? Itereable 是一个单一方法接口,仅此而已。 “快速”迭代并不是很快。这只是一个打字快捷方式。【参考方案4】:

第一个称为enhanced-for,或“for each”。它被认为是 for 语句的传统模型的语法糖 - 允许您迭代集合中的 每个 元素 - 无论是数组还是 Java 集合框架中的任何内容。

这些类型的循环有两个明显的区别:

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

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

...否则,表达式必须具有数组类型,T[]。

L1 ... Lm 是紧接在增强的 for 语句之前的标签序列(可能为空)。

增强的 for 语句相当于基本的 for 语句,形式如下:

T[] #a = Expression; 
L1: L2: ... Lm:
for (int #i = 0; #i < #a.length; #i++) 
    VariableModifiers(opt) TargetType Identifier = #a[#i];
    Statement

#a 和 #i 是自动生成的标识符,与增强 for 语句发生时范围内的任何其他标识符(自动生成或其他方式)不同。

【讨论】:

【参考方案5】:

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

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

for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) 
  String s = iterator.next();
  write.append(s);
  write.append('\n');

【讨论】:

这仅适用于实现Iterable 的集合。数组没有实现Iterable,所以这不适用。 而且是错误的,因为没有getIterator方法。 @Makoto,问题是“我只是不明白如何使用*方法 1。”。这是我的回答。 @MarkoTopolnik,如果你说getIterator 应该是iterator,那将是一个更有用的评论。我不认为拼写错误会导致答案错误。 还是错了,因为list不是迭代器变量的名字。顺便说一句,“它”是指“代码”而不是“答案”。另外,如果我无法找到对您个人最有用的正确解释方式,我深表歉意。

以上是关于为啥循环遍历 ArrayList<String> 与常规数组相同? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

如何从 HashMap 中提取 ArrayList 并在 Java 中循环遍历它?

ArrayList和LinkedList循环遍历效率探索

求教arraylist里面放map,怎么循环遍历得到map里面的数据,如:List<Map<String, String>> list = new Ar

普通for循环遍历LinkedList弊端

不要用for循环去遍历LinkedList

foreach循环原理