1. 概述

对于开发者来说,从数据结构中查找元素的索引是一项常见任务。本教程将使用Java 8流API和第三方库,通过一个布尔条件找到列表中的第一个匹配元素的索引。

2. 准备工作

在本文中,我们将使用下面提到的User对象编写一些测试用例来达到目标:

public class User {

    private String userName;
    private Integer userId;

   // constructor and getters
}

此外,我们将创建一个ArrayList,包含User对象,以便在所有测试用例中使用。接下来,我们将找到名字为“John”的第一个用户的索引:

List<User> userList = List.of(new User(1, "David"), new User(2, "John"), new User(3, "Roger"), new User(4, "John"));

String searchName = "John";

3. 使用Java流API

Java流API是Java 8引入的优秀特性之一。它提供了众多方法来迭代、过滤、映射、匹配和收集数据。考虑到这一点,我们将利用这些方法从列表中查找索引。

3.1. 使用stream()filter()

让我们使用Stream类的基本功能编写一个测试用例,以获取索引:

@Test
public void whenUsingStream_thenFindFirstMatchingUserIndex() {
    AtomicInteger counter = new AtomicInteger(-1);
    int index = userList.stream()
      .filter(user -> {
          counter.getAndIncrement();
          return searchName.equals(user.getUserName());
      })
      .mapToInt(user -> counter.get())
      .findFirst()
      .orElse(-1);

    assertEquals(1, index);
}

在这里,我们可以从列表创建一个流,并应用filter()方法。在filter()方法内部,我们使用AtomicInteger递增计数器来追踪元素的索引。最后,我们将计数器值映射并使用findFirst()方法获取第一个匹配元素的索引。

3.2. 使用IntStream

另一种选择是使用IntStream类遍历列表元素,使用上述章节中提到的类似逻辑来获取索引:

@Test
public void whenUsingIntStream_thenFindFirstMatchingUserIndex() {
    int index = IntStream.range(0, userList.size())
      .filter(streamIndex -> searchName.equals(userList.get(streamIndex).getUserName()))
      .findFirst()
      .orElse(-1);
    assertEquals(1, index);
}

3.3. 使用Stream takeWhile()

takeWhile()方法返回直到给定条件为true的数据。一旦条件失败,它会停止迭代以收集迭代到的数据:

@Test
public void whenUsingTakeWhile_thenFindFirstMatchingUserIndex() {
    long predicateIndex = userList.stream()
      .takeWhile(user -> !user.getUserName().equals(searchName))
      .count();
    assertEquals(1, predicateIndex);
}

上述示例显示,takeWhile()方法收集元素,直到找到名为“John”的User对象,然后停止迭代。之后,我们可以使用count()方法获取第一个匹配元素的索引。

在没有匹配元素的情况下,例如列表中不存在匹配项,迭代将继续到最后一个元素,输出值为4,即从输入列表中迭代的总元素数量:

@Test
public void whenUsingTakeWhile_thenFindIndexFromNoMatchingElement() {
    long predicateIndex = userList.stream()
      .takeWhile(user -> !user.getUserName().equals(searchName))
      .count();
    assertEquals(4, predicateIndex);
}

takeWhile()方法是在Java 9中引入的。

4. 使用第三方库

尽管Java流API足以实现我们的目标,但它仅从Java 1.8版本开始可用。如果应用程序在较旧的Java版本上运行,那么外部库就变得很有用。

4.1. Google Guava的Iterables

我们需要在pom.xml中添加最新的Maven依赖

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>33.0.0-jre</version>
</dependency>

Guava库中的Iterables类有一个名为indexOf()的方法,该方法返回指定可迭代对象中第一个满足给定谓词的元素的索引:

@Test
public void whenUsingGoogleGuava_thenFindFirstMatchingUserIndex() {
    int index = Iterables.indexOf(userList, user -> searchName.equals(user.getUserName()));
    assertEquals(1, index);
}

4.2. Apache Commons Collections的IterableUtils

同样,Apache Commons Collections库中的IterableUtils类也提供了获取索引的功能。我们在pom.xml中添加Apache Commons Collections的Maven依赖

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.4</version>
</dependency>

IterableUtils.indexOf()方法接受一个可迭代集合和一个谓词,然后返回第一个匹配元素的索引:

@Test
public void whenUsingApacheCommons_thenFindFirstMatchingUserIndex() {
    int index = IterableUtils.indexOf(userList, user -> searchName.equals(user.getUserName()));
    assertEquals(1, index);
}

两个库中的indexOf()方法如果没有元素满足谓词条件,则返回-1。

5. 总结

在这篇文章中,我们学习了多种方法,用于在列表中查找满足布尔条件的第一个元素的索引。我们使用了Java流API、Google Guava的Iterables类以及Apache Commons Collections的IterableUtils类。

如往常一样,参考代码可在GitHub上找到。