概述

迭代是编程的基础,它使开发者能够遍历并轻松操作数据结构。然而,在某些情况下,我们可能需要在遍历集合时跳过第一个元素。本教程将探讨使用循环和Java 8流API的各种方法来实现这一目标。

1. 跳过第一个元素的方法

在Java中,当我们遍历集合时,有多种方式可以跳过第一个元素。以下是三种主要方法:使用标准循环、使用Iterator接口以及利用流API。

有些算法需要跳过第一个元素,原因可能是单独处理或完全忽略,例如CSV表头。计算小型企业每日收入的累积差额就是一个适合这种做法的例子:

从第二个元素开始迭代有助于我们创建简单算法,并避免不必要的复杂检查。

2.1. for循环

最简单的跳过第一个元素的方法是使用for循环,将计数器变量从1开始而不是0。这种方法最适合支持索引访问的集合,如ArrayList和简单的数组:

void skippingFirstElementInListWithForLoop(List<String> stringList) {
    for (int i = 1; i < stringList.size(); i++) {
        process(stringList.get(i));
    }
} 

这个方法的主要优点是其简洁性。同时,如果需要,我们还可以访问第一个值。然而,设置初始值可能会被忽视。

2.2. while循环

另一种方法是使用while循环配合Iterator。我们可以手动将迭代器向前推进以跳过第一个元素:

void skippingFirstElementInListWithWhileLoop(List<String> stringList) {
    Iterator<String> iterator = stringList.iterator();
    if (iterator.hasNext()) {
        iterator.next();
    }
    while (iterator.hasNext()) {
        process(iterator.next());
    }
}

这种方法的一个优点是它适用于所有Iterable集合,如Set

void skippingFirstElementInSetWithWhileLoop(Set<String> stringSet) {
    Iterator<String> iterator = stringSet.iterator();
    if (iterator.hasNext()) {
        iterator.next();
    }
    while (iterator.hasNext()) {
        process(iterator.next());
    }
}

另一个好处是,我们可以将集合抽象到最通用的类(在这种情况下可能是Iterable),从而重用代码。然而,由于Set通常不维护元素顺序,不清楚我们会跳过哪个元素。另一方面,如果我们需要第一个元素,必须显式存储。

2.3. 流API

Java 8引入了流API,提供了一种更声明式的处理集合的方式。要跳过第一个元素,我们可以使用skip()方法:

void skippingFirstElementInListWithStreamSkip(List<String> stringList) {
    stringList.stream().skip(1).forEach(this::process);
}

与前一种方法类似,这种方法适用于Iterable集合,而且关于其意图的表述较为明确。我们可以轻松地使用Set

void skippingFirstElementInSetWithStreamSkip(Set<String> stringSet) {
    stringSet.stream().skip(1).forEach(this::process);
}

同样,也可以用于Map

void skippingFirstElementInMapWithStreamSkip(Map<String, String> stringMap) {
    stringMap.entrySet().stream().skip(1).forEach(this::process);
}

Set一样,Map也不维护元素顺序,因此在不确定的情况下使用时要小心。不过,在某些情况下,跳过SetMap中的一个元素可能是有用的。

2.4. 使用subList()

对于List,另一种跳过第一个元素的方法是使用subList()。此方法返回从指定的开始索引(包括)到结束索引(不包括)的列表片段。结合for-each循环,我们可以轻松跳过第一个元素:

void skippingFirstElementInListWithSubList(List<String> stringList) {
    for (final String element : stringList.subList(1, stringList.size())) {
        process(element);
    }
}

需要注意的是,subList()在空列表上可能会失败。此外,对于刚接触Java的人来说,它并不会创建一个新的集合,而是原始列表的视图,这一点可能不太明显。总的来说,这是一种非常命令式的遍历方式。

2.5. 其他方法

尽管基本的遍历方法有限,但我们可以组合和修改它们来获得更多的变体。这些变体虽然不显著不同,但我们列出它们是为了展示可能的策略和激发实验。

我们可以使用带有额外条件的for循环来跳过第一个元素,这在我们需要分别处理第一个元素时很有用:

void skippingFirstElementInListWithForLoopWithAdditionalCheck(List<String> stringList) {
    for (int i = 0; i < stringList.size(); i++) {
        if (i == 0) {
            // do something else
        } else {
            process(stringList.get(i));
        }
    }
}

我们可以将for循环中的逻辑应用到while循环中,并在其中使用计数器:

void skippingFirstElementInListWithWhileLoopWithCounter(List<String> stringList) {
    int counter = 0;
    while (counter < stringList.size()) {
        if (counter != 0) {
            process(stringList.get(counter));
        }
        ++counter;
    }
}

这种方法在根据内部逻辑以不同的步长前进列表时可能会有所帮助。

还可以将这种方法与subList和流结合起来:

void skippingFirstElementInListWithStreamSkipAndSubList(List<String> stringList) {
    stringList.subList(1, stringList.size()).forEach(this::process);
}

总的来说,我们的想象力可能会带来一些完全不应该使用的奇特决策:

void skippingFirstElementInListWithReduce(List<String> stringList) {
    stringList.stream().reduce((skip, element) -> {
        process(element);
        return element;
    });
}

3. 结论

虽然Java提供了多种遍历集合并跳过第一个元素的方法,选择合适的方法的关键是代码的清晰度。因此,我们应该优先考虑两种简单且较为冗余的方法:简单的for循环或stream.skip()。其他方法更为复杂,包含更多步骤,在可能的情况下应尽量避免。如往常一样,本文中的示例可在GitHub上的教程仓库中找到。