拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 如何在每个循环中访问迭代计数器

如何在每个循环中访问迭代计数器

白鹭 - 2021-11-24 444 0 0

1.概述

在Java中遍历数据时,我们可能希望同时访问当前项及其在数据源中的位置。

for循环中很容易实现,在经典的for循环中,位置通常是循环计算的重点,但是当我们为每个循环或流使用类似的构造时,它需要做更多的工作。

在这个简短的教程中,我们将看看几个方法是for每一个操作可以包括计数器。

2.实现计数器

让我们从一个简单的例子开始。我们将获取电影的有序列表,并输出其排名。

List<String> IMDB_TOP_MOVIES = Arrays.asList("The Shawshank Redemption",

 "The Godfather", "The Godfather II", "The Dark Knight");

2.1。 for循环

for循环使用一个计数器来引用当前项目,因此这是一种对列表中的数据及其索引进行操作的简便方法:

List rankings = new ArrayList<>();

 for (int i = 0; i < movies.size(); i++) {

 String ranking = (i + 1) + ": " + movies.get(i);

 rankings.add(ranking);

 }

由于此List可能是ArrayList ,因此get操作很有效,并且上面的代码是解决我们问题的简单方法。

assertThat(getRankingsWithForLoop(IMDB_TOP_MOVIES))

 .containsExactly("1: The Shawshank Redemption",

 "2: The Godfather", "3: The Godfather II", "4: The Dark Knight");

但是,并非所有Java数据源都可以通过这种方式进行迭代。有时get是一项耗时的操作,或者我们只能使用StreamIterable.

2.2。 for每个循环

我们将继续使用电影列表,但假设我们只能对每个结构使用Java对其进行迭代:

for (String movie : IMDB_TOP_MOVIES) {

 // use movie value

 }

在这里,我们需要使用一个单独的变量来跟踪当前索引。我们可以在循环外部构造它,然后在内部增加它:

int i = 0;

 for (String movie : movies) {

 String ranking = (i + 1) + ": " + movie;

 rankings.add(ranking);



 i++;

 }

我们应该注意,在循环中使用完计数器后,我们必须增加它的数量。

3.一种功能forEach

每次我们需要编写计数器扩展名时,都可能导致代码重复,并可能冒着有关何时更新计数器变量的意外错误的风险。因此,我们可以使用Java的功能接口来概括上述内容。

首先,我们应该将循环内部的行为视为集合中项目和索引的使用者。可以使用BiConsumer进行建模,该函数定义了一个accept函数,该函数接受两个参数

@FunctionalInterface

 public interface BiConsumer<T, U> {

 void accept(T t, U u);

 }

由于循环的内部使用了两个值,因此我们可以编写一个通用的循环操作。它可能需要Iterable (每个循环将在BiConsumer ,以对每个项目及其索引执行该操作。我们可以使用类型参数T来使它通用:

static <T> void forEachWithCounter(Iterable<T> source, BiConsumer<Integer, T> consumer) {

 int i = 0;

 for (T item : source) {

 consumer.accept(i, item);

 i++;

 }

 }

BiConsumer的实现提供为lambda,我们可以在电影排名示例中使用它:

List rankings = new ArrayList<>();

 forEachWithCounter(movies, (i, movie) -> {

 String ranking = (i + 1) + ": " + movies.get(i);

 rankings.add(ranking);

 });

4.流实现添加一个计数器来forEach

Java Stream API允许我们表达数据如何通过过滤器和转换。它还提供了forEach函数。让我们尝试将其转换为包含计数器的操作。

Stream forEach函数需要Consumer处理下一个项目。但是,我们可以创建该Consumer来跟踪计数器并将该项目传递到BiConsumer

public static <T> Consumer<T> withCounter(BiConsumer<Integer, T> consumer) {

 AtomicInteger counter = new AtomicInteger(0);

 return item -> consumer.accept(counter.getAndIncrement(), item);

 }

该函数返回一个新的lambda。该lambda使用AtomicInteger对像在迭代过程中跟踪计数器。每次有新项目时都会调用getAndIncrement

由该函数创建的lambda委托给BiConsumer ,以便算法可以处理项目及其索引。

让我们来看看在使用我们的电影排名对一个例子Stream称为movies

List rankings = new ArrayList<>();

 movies.forEach(withCounter((i, movie) -> {

 String ranking = (i + 1) + ": " + movie;

 rankings.add(ranking);

 }));

forEach withCounter函数的调用,以创建一个对象,该对象既可以跟踪计数,又可以充当Consumer ,因为forEach操作也可以传递其值。

5.结论

在这篇简短的文章中,我们研究for每种操作将计数器附加到Java的三种方法。

我们看到了如何跟踪他们每个执行当前项目的索引for一个循环。然后,我们研究了如何概括这种模式以及如何将其添加到流操作中。

标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *