Contents

在列表中找到元素

1. 概述

在列表中查找元素是我们作为开发人员遇到的一项非常常见的任务。

在本快速教程中,我们将介绍使用 Java 执行此操作的不同方法。

2. 设置

首先让我们从定义一个Customer POJO 开始:

public class Customer {
    private int id;
    private String name;
    
    // getters/setters, custom hashcode/equals
}

然后是客户的ArrayList

List<Customer> customers = new ArrayList<>();
customers.add(new Customer(1, "Jack"));
customers.add(new Customer(2, "James"));
customers.add(new Customer(3, "Kelly"));

请注意,我们已经 在Customer类中覆盖了hashCode 和equals

根据我们当前的equals实现,具有相同id的两个Customer对象将被视为相等。

我们将 一路使用此客户列表。

3. 使用Java API

Java 本身提供了几种在列表中查找项目的方法:

  • contains方法
  • indexOf方法
  • for 循环
  • Stream API

3.1. contains()

List 公开了一个名为 contains的方法:

boolean contains(Object element)

顾名思义,如果列表包含指定元素,此方法返回true ,否则 返回false

因此,当我们需要检查列表中是否存在特定项目时,我们可以:

Customer james = new Customer(2, "James");
if (customers.contains(james)) {
    // ...
}

3.2. indexOf()

indexOf 是另一种查找元素的有用方法:

int indexOf(Object element)

此方法返回给定列表中指定元素第一次出现的索引,如果列表不包含该element,则返回 -1 。

因此,从逻辑上讲,如果此方法返回 -1 以外的任何值,我们就知道列表包含该元素:

if(customers.indexOf(james) != -1) {
    // ...
}

使用这种方法的主要优点是它可以告诉我们指定元素在给定列表中的位置。

3.3. 基本循环

现在,如果我们想要对元素进行基于字段的搜索怎么办?例如,假设我们要宣布抽奖,我们需要将具有特定名称的customer声明为中奖者。

对于这种基于字段的搜索,我们可以转向迭代。

遍历列表的一种传统方式是使用Java 的一种循环 结构。在每次迭代中,我们将列表中的当前项目与我们正在寻找的元素进行比较,看看它是否匹配:

public Customer findUsingEnhancedForLoop(
  String name, List<Customer> customers) {
    for (Customer customer : customers) {
        if (customer.getName().equals(name)) {
            return customer;
        }
    }
    return null;
}

这里的name是指我们在给定的customer列表中搜索的名称。此方法返回列表中具有匹配名称的第一个Customer对象,如果不存在这样的Customer,则返回null

3.4. 使用iterator循环

iterator  是我们可以遍历项目列表的另一种方式。

我们可以简单地拿我们之前的例子做一些调整:

public Customer findUsingIterator(
  String name, List<Customer> customers) {
    Iterator<Customer> iterator = customers.iterator();
    while (iterator.hasNext()) {
        Customer customer = iterator.next();
        if (customer.getName().equals(name)) {
            return customer;
        }
    }
    return null;
}

因此,行为与以前相同。

3.5. Java 8 Stream API

从 Java 8 开始,我们还可以 使用Stream API  在List 中查找元素。

要在给定列表中找到匹配特定条件的元素,我们:

  • 在列表中调用stream()
  • 使用适当的谓词调用 filter()方法
  • 调用 findAny()构造, 如果存在这样的元素,则返回与**包裹在Optional中的过滤谓词匹配的第 一个元素
Customer james = customers.stream()
  .filter(customer -> "James".equals(customer.getName()))
  .findAny()
  .orElse(null);

为方便起见,如果Optional为空,我们默认为null,但这可能并不总是适用于所有场景的最佳选择。

4. 第三方库

现在,虽然 Stream API 已经绰绰有余,但如果我们卡在 Java 的早期版本上该怎么办?

幸运的是,我们可以使用许多第三方库,例如 Google Guava 和 Apache Commons。

4.1. Guava

Google Guava 提供的功能类似于我们可以使用流执行的功能:

Customer james = Iterables.tryFind(customers,
  new Predicate<Customer>() {
      public boolean apply(Customer customer) {
          return "James".equals(customer.getName());
      }
  }).orNull();

就像使用Stream API 一样,我们可以选择返回默认值而不是null

Customer james = Iterables.tryFind(customers,
  new Predicate<Customer>() {
      public boolean apply(Customer customer) {
          return "James".equals(customer.getName());
      }
  }).or(customers.get(0));

如果没有找到匹配项,上面的代码将选择列表中的第一个元素。

另外,如果列表或谓词为null,不要忘记 Guava 会抛出NullPointerException

4.2. Apache Commons

我们可以使用 Apache Commons 以几乎完全相同的方式找到一个元素:

Customer james = IterableUtils.find(customers,
  new Predicate<Customer>() {
      public boolean evaluate(Customer customer) {
          return "James".equals(customer.getName());
      }
  });

但是有几个重要的区别:

  1. 如果我们传递一个null列表,Apache Commons 只会返回null 值。
  2. 它不提供像 Guava 的tryFind 那样的默认值功能。