【问题标题】:Enhanced for loop starting part way through List增强的 for 循环从 List 开始
【发布时间】:2013-06-22 12:47:51
【问题描述】:

我是 Java 初学者,我有这个疑问。是否可以在 ArrayList 上使用 Java 中的增强型 for 循环,但从指定点而不是 ArrayList[0] 开始。

For eg. ArrayList<Integer> calc = new ArrayList<Integer>;
       // calc contains {0,1,2,3,4,5,6,7}

我可以使用增强的 for 循环并从 calc[2] 而不是 calc[0] 开始迭代吗?如果可能的话,我该怎么做? 在我的特定情况下,使用增强的 for 循环会更好,而不是普通的 for 循环。

【问题讨论】:

  • 当我需要索引时,不管是什么原因,我从不使用增强的 for 循环。增强的 for 与 c# 中的 foreach 完全相同:它循环包含在列表中的每个对象并处理它们。
  • 为什么你认为增强的 for 循环会更好?
  • @duffymo 我基本上想遍历 ArrayList ......而增强的 for 循环是最好的......但我想从不同的点而不是 ArrayList [0] 开始。 ..所以我想知道是否可以修改增强的 for 循环以适合我的目的?
  • 不需要“基本上”。我知道你想要什么。我不同意你坚持认为增强循环是“最好的”。不,不能修改。使用传统的 for 循环并完成它。
  • BTW 建议尽可能使用集合接口而不是实际实现,所以不要使用 ArrayList calc = new ArrayList();我建议使用 List calc = new ArrayList();这样,从一种列表实现切换到另一种实现只是一个地方的变化(以及考虑后果的时间,尤其是时间复杂度)。

标签: java list for-loop


【解决方案1】:

Java 中最好的方法是这样的:

for (Integer i : calc.subList(start, calc.size()) {
  ... 
}

subList 是原始列表的高效视图,因此它几乎正是您所需要的。

更新

好的,受 Mikera 的 cmets 启发,我在 jmh 上对其进行了基准测试。这是基准代码:

import org.openjdk.jmh.annotations.GenerateMicroBenchmark;

public class Benchmark1
{
  static final List<Integer> list = new ArrayList(asList(1,2,3,4,5,6,7,8,9,10));
  static { for (int i = 0; i < 5; i++) list.addAll(list); }

  @GenerateMicroBenchmark
  public long testIterator() {
    long sum = 0;
    for (int i : list) sum += i;
    return sum;
  }
  @GenerateMicroBenchmark
  public long testIndexed() {
    long sum = 0;
    for (int i = 0; i < list.size(); i++) sum += list.get(i);
    return sum;
  }
  @GenerateMicroBenchmark
  public long testSublistIterator() {
    long sum = 0;
    for (int i : list.subList(1, list.size())) sum += i;
    return sum;
  }
  @GenerateMicroBenchmark
  public long testIndexedSublist() {
    long sum = 0;
    final List<Integer> l = list.subList(1, list.size());
    for (int i = 0; i < l.size(); i++) sum += l.get(i);
    return sum;
  }
}

结果如下:

Benchmark        ops/msec
-------------------------
Indexed          1860.982
IndexedSublist   1642.059
Iterator         1818.657
SublistIterator  1496.994

结论:

  1. enhanced for 在主列表上的速度与索引迭代一样快,一旦超过初始化成本;

  2. 子列表的遍历有点比主列表慢,迭代有点比索引遍历慢;

  3. 对于所有实际用途,所有差异都可以忽略不计。

【讨论】:

  • “最佳”取决于您是否关心简洁性或性能,我相信。与传统的索引循环相比,subList 增加了少量开销。
  • 不,“最佳”取决于问题所在:使用增强的 for。但是,在性能页面上,只有在 ArrayList 的静态类型上进行索引迭代才可接受;否则你将面临 O(n2) 崩溃的风险。
  • 好吧,如果你想玩迂腐, List.subList() 可能比 O(n^2) 更糟糕 - 你无法保证它是如何实现的 :-) 问题确实指定了然而,ArrayList,所以我们都是安全的。
  • 既然您提到了规范,我正在阅读它...这句话很有趣,因为它表明subList 专门旨在帮助一个用例正如我们的:“这种方法消除了对显式范围操作(通常存在于数组中的那种)的需要。任何需要列表的操作都可以通过传递子列表视图而不是整个列表来用作范围操作。”这更强烈地暗示,任何理智的实施都会以低廉的成本完成这项工作。
  • +1 不错 ;-) 小心总是返回相同结果的东西。它经常被优化掉。我通常使用随机生成器,也使用基线方法,只进行随机生成以获得比较点。
【解决方案2】:

你在这里使用传统的循环卡住了......

for (int i = 2; i < calc.size(); i++) {
    Integer x = calc.get(i);
}

好吧,除非您愿意创建一个临时的 subList 只是为了使用增强的 for 循环,这很好,因为子列表是原始列表的视图,不会创建新的列表对象:

for (Integer x : calc.subList(2, calc.size())) {

}

【讨论】:

    【解决方案3】:
    for(Item e : list.subList(1, list.size())) {
        // ...
    }
    

    【讨论】:

    • 我觉得应该是list.size()而不是list.size() - 1,因为subList()toIndex的第二个参数是独占的,不是包容的。
    • @Adam 是的。更新了,以防万一。 :-)
    【解决方案4】:

    你可以遍历sublist,如下:

    for (Integer integerMember : calc.subList(2, calc.size()) {
        // operation here
    }
    

    【讨论】:

      【解决方案5】:

      需要注意的是,增强的 for 循环对于 ArrayList 的效率低于标准索引。这是因为我们必须创建一个Iterator 对象并在循环的每一步都调用hasNextnext,造成不必要的开销。

      因此,我建议以传统方式进行索引,而不是使用sublist 方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-02-23
        • 1970-01-01
        • 2011-01-20
        • 1970-01-01
        • 1970-01-01
        • 2012-08-22
        • 1970-01-01
        相关资源
        最近更新 更多