Java8:當 forEach 需要索引

Java8:當 forEach 需要索引

Java8:當 forEach 需要索引


在 上一篇文章 中,我們討論瞭如何使用 Java8 中 Map 添加的新方法 computeIfAbsent 來統計集合中每個元素出現的所有位置,代碼如下:

public static Map<string>> getElementPositions(List<string> list) {
Map<string>> positionsMap = new HashMap<>();

for (int i = 0; i < list.size(); i++) {
positionsMap.computeIfAbsent(list.get(i), k -> new ArrayList<>(1)).add(i);
}

return positionsMap;
}
/<string>/<string>/<string>

至少有兩點需要探討:
1、如果 list 不是基於數組的(即不是 RandomAccess 的),而是基於鏈表的,那麼 list.get(int index) 方法的效率就值得思考了;
2、既然都有了 Lambda(即當前平臺為 Java8),我們為什麼還要一次次去寫傳統的 for 循環呢?

在 Java8 中,為 Iterable 接口添加了默認的 forEach 方法:

Java8:當 forEach 需要索引

Iterable 接口默認的 forEach 方法


很好理解,遍歷當前 Iterable 中所有的元素,使用每個元素作為參數調用一次 action。而 Collection 接口繼承了 Iterable 接口,所以所有的繼承自 Collection 的集合類都可以直接調用 forEach 方法。比如:

public static void main(String[] args) throws Exception {
List<string> list = Arrays.asList("a", "b", "b", "c", "c", "c", "d", "d", "d", "f", "f", "g");

list.forEach(str -> System.out.print(str + " "));

System.out.println();
}
/<string>

運行結果:

Java8:當 forEach 需要索引

forEach 運行示例

那如果我們在遍歷的時候需要使用到元素的索引呢(類似 getElementPositions 方法那樣)?
很可惜,Java8的 Iterable 並沒有提供一個帶索引的 forEach 方法。不過自己動手,豐衣足食——讓我們自己寫一個帶索引的 forEach 方法:

import java.util.Objects;
import java.util.function.BiConsumer;

/**
* Iterable 的工具類
*/
public class Iterables {

public static void forEach(
Iterable extends E> elements, BiConsumer<integer> action) {
Objects.requireNonNull(elements);
Objects.requireNonNull(action);

int index = 0;
for (E element : elements) {
action.accept(index++, element);
}
}
}
/<integer>

該 forEach 方法第一個參數為要遍歷的 Iterable,第二個參數為 BiConsumer。BiConsumer 的輸入參數第一個即索引,第二個為元素。

我們測試下這個 forEach 方法:

public static void main(String[] args) throws Exception {

List<string> list = Arrays.asList("a", "b", "b", "c", "c", "c", "d", "d", "d", "f", "f", "g");

Iterables.forEach(list, (index, str) -> System.out.println(index + " -> " + str));
}
/<string>

運行結果:

Java8:當 forEach 需要索引

測試 Iterables.forEach 方法的運行結果

結果和預期的一致。

現在我們使用 Iterables.forEach 改寫 getElementPositions方法:

public static Map<string>> getElementPositions(List<string> list) {
Map<string>> positionsMap = new HashMap<>();

Iterables.forEach(list, (index, str) -> {
positionsMap.computeIfAbsent(str, k -> new ArrayList<>(1)).add(index);

});

return positionsMap;
}

public static void main(String[] args) throws Exception {
List<string> list = Arrays.asList("a", "b", "b", "c", "c", "c", "d", "d", "d", "f", "f", "g");

System.out.println("使用 computeIfAbsent 和 Iterable.forEach:");
Map<string>> elementPositions = getElementPositions(list);
System.out.println(elementPositions);
}
/<string>/<string>/<string>/<string>/<string>

運行結果和原來一致:

Java8:當 forEach 需要索引

使用 computeIfAbsent 和 Iterable.forEach 的運行結果

真的不明白這麼簡單且實用的方法,Java8 為什麼不在 Iterable 中提供一個默認實現(此處應有黑人問號)。

文末

歡迎關注個人公眾號:Coder編程
歡迎關注Coder編程公眾號,主要分享數據結構與算法、Java相關知識體系、框架知識及原理、Spring全家桶、微服務項目實戰、DevOps實踐之路、每日一篇互聯網大廠面試或筆試題以及PMP項目管理知識等。更多精彩內容正在路上~

Java8:當 forEach 需要索引


分享到:


相關文章: