概述
迭代是编程的基础,它使开发者能够遍历并轻松操作数据结构。然而,在某些情况下,我们可能需要在遍历集合时跳过第一个元素。本教程将探讨使用循环和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
也不维护元素顺序,因此在不确定的情况下使用时要小心。不过,在某些情况下,跳过Set
或Map
中的一个元素可能是有用的。
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上的教程仓库中找到。