除第一个元素外,迭代列表的首选方法是什么?

13

我经常需要循环遍历一个列表,从第二个元素开始。 例如,这是一列:

List<String> column = Arrays.asList("HEADER", "value1", "value2", "value3");

我只需要打印数值。

我看到有三种方法:

  1. 使用子列表:

    for (String s : column.subList(1, column.size())) {
        System.out.println(s);
    }
    
  2. 使用ListIterator

    for (ListIterator<String> iter = column.listIterator(1); iter.hasNext(); ) {
        System.out.println(iter.next());
    }
    
  3. 使用索引

  4. for (int i = 1; i < column.size(); i++) {
        System.out.println(column.get(i));
    }
    

就易读性、最佳实践和性能而言,哪一个是最受欢迎的?

我认为子列表解决方案更易读,但实践中很少见到它。 与索引解决方案相比,它有什么显著的缺陷吗?


都可以,我更喜欢按照你给出的顺序。使用流的答案也可以,但它并不容易阅读。 - CoronA
3个回答

11

如果您使用Java 8或更高版本,您可以使用:

column.stream().skip(1).forEach((c) -> System.out.println(c))

1
你的解决方案也不错,但我不能接受这个答案,因为它缺乏与其他方法的比较。 - Evgeny Kharitonov
2
如果你关心顺序,请务必使用 forEachOrdered - Peter Graham

5

这实际上只是一个(几乎)关于“个人风格”的问题,你应该选择最适合你/你的团队的选项。

选项3似乎带来的额外开销最少 - 选项1和选项2都创建新的“中间”对象(分别是子列表和迭代器)。并且要明确一点:sublist()不会创建一个新的列表并填充它 - 它归结为一个新的Sublist对象,该对象仅“知道”较大父列表中的边界。

但是: 如果我们考虑不支持随机访问列表元素(例如链表)的列表,则与其他选项相比,选项3会导致大量性能成本。


选项1不会创建一个新的对象,只会返回原始列表的一个视图。 - zaerymoghaddam
那个“视图”子列表对象不是一个新对象吗?但我修改了我的答案以使其更精确;-) - GhostCat
是的,你说得对。我更多地考虑的是作为基础列表使用的实际对象引用 ;) - zaerymoghaddam
1
然而,选项3依赖于随机访问列表。使用LinkedList将会使选项3变得昂贵。 - CoronA
1
如果列表不是RandomAccess,选项3的性能将非常糟糕。如果列表是从多个线程访问的并发列表,则不安全。虽然这里不是这种情况,但基于迭代器的迭代通常更安全和快速。 - JB Nizet
@CoronA和JB都是正确的...只是我忘记了。已经相应地更新了答案。 - GhostCat

1
这更多是个人偏好的问题。 sublist方法不会返回新对象,只会在当前列表上创建一个视图,因此它没有任何内存开销。我认为可读性是唯一决定因素,最后一种选项是最可读的解决方案,不需要调用任何额外的方法(例如迭代器、子列表等)。

1
sublist()确实会返回一个新对象 - 就是你所说的那个视图;-) - GhostCat
1
但我认为“我们”都知道你的意思...我没有得到我的第一个早上点赞,所以我总是有点挑剔;-) - GhostCat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接