Java8 给 forEach 添加索引

一,需求

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]}

发布了162 篇原创文章 · 获赞 47 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/echizao1839/article/details/103683397
今日推荐