一,需求
1、如果 list 不是基于数组的(即不是 RandomAccess 的),而是基于链表的,那么 list.get(int index) 方法的效率就值得思考了;
2、既然都有了 Lambda(即当前平台为 Java8),我们为什么还要一次次去写传统的 for 循环呢?
二,实现
在 Java8 中,为 Iterable 接口添加了默认的 forEach 方法
/**
* Performs the given action for each element of the {@code Iterable}
* until all elements have been processed or the action throws an
* exception. Actions are performed in the order of iteration, if that
* order is specified. Exceptions thrown by the action are relayed to the
* caller.
* <p>
* The behavior of this method is unspecified if the action performs
* side-effects that modify the underlying source of elements, unless an
* overriding class has specified a concurrent modification policy.
*
* @implSpec
* <p>The default implementation behaves as if:
* <pre>{@code
* for (T t : this)
* action.accept(t);
* }</pre>
*
* @param action The action to be performed for each element
* @throws NullPointerException if the specified action is null
* @since 1.8
*/
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
很好理解,遍历当前 Iterable 中所有的元素,使用每个元素作为参数调用一次 action。而 Collection 接口继承了 Iterable 接口,所以所有的继承自 Collection 的集合类都可以直接调用 forEach 方法。比如
@Test
void test1() {
List<String> list = Arrays.asList("a", "b", "b", "c", "c", "c", "d", "d", "d", "f", "f", "g");
list.forEach(str -> System.out.print(str + " "));
}
返回:a b b c c c d d d f f g
那如果我们在遍历的时候需要使用到元素的索引呢?
很可惜,Java8的 Iterable 并没有提供一个带索引的 forEach 方法。让我们自己写一个带索引的 forEach 方法。
package com.g7go.subjectdemo;
import java.util.Objects;
import java.util.function.BiConsumer;
/**
* Iterable 的工具类
*
* @author lwc
*/
public class Iterables {
public static <E> void forEach(Iterable<? extends E> elements, BiConsumer<Integer, ? super E> action) {
Objects.requireNonNull(elements);
Objects.requireNonNull(action);
int index = 0;
for (E element : elements) {
action.accept(index++, element);
}
}
}
该 forEach 方法第一个参数为要遍历的 Iterable,第二个参数为 BiConsumer。BiConsumer 的输入参数第一个即索引,第二个为元素。
我们测试下这个 forEach 方法
@Test
void test2() {
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));
}
返回:
0 -> a
1 -> b
2 -> b
3 -> c
4 -> c
5 -> c
6 -> d
7 -> d
8 -> d
9 -> f
10 -> f
11 -> g
给定一个 List<String>,统计每个元素出现的所有位置。比如,给定 list:["a", "b", "b", "c", "c", "c", "d", "d", "d", "f", "f", "g"],那么应该返回:
a : [0]
b : [1, 2]
c : [3, 4, 5]
d : [6, 7, 8]
f : [9, 10]
g : [11]
@Test
void test3() {
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, List<Integer>> elementPositions = getElementPositions(list);
System.out.println(elementPositions);
}
public static Map<String, List<Integer>> getElementPositions(List<String> list) {
Map<String, List<Integer>> positionsMap = new HashMap<>();
Iterables.forEach(list, (index, str) -> {
positionsMap.computeIfAbsent(str, k -> new ArrayList<>(1)).add(index);
});
return positionsMap;
}
返回:
使用 computeIfAbsent 和 Iterable.forEach:
{a=[0], b=[1, 2], c=[3, 4, 5], d=[6, 7, 8], f=[9, 10], g=[11]}