将 Java 流筛选为 1 个且只有 1 个元素

2022-08-31 05:10:27

我正在尝试使用Java 8 Streams来查找.但是,我想保证与筛选条件匹配一个且只有一个匹配项。LinkedList

取此代码:

public static void main(String[] args) {

    LinkedList<User> users = new LinkedList<>();
    users.add(new User(1, "User1"));
    users.add(new User(2, "User2"));
    users.add(new User(3, "User3"));

    User match = users.stream().filter((user) -> user.getId() == 1).findAny().get();
    System.out.println(match.toString());
}

static class User {

    @Override
    public String toString() {
        return id + " - " + username;
    }

    int id;
    String username;

    public User() {
    }

    public User(int id, String username) {
        this.id = id;
        this.username = username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public int getId() {
        return id;
    }
}

此代码根据其 ID 查找 。但是不能保证有多少 s 与过滤器匹配。UserUser

将过滤线更改为:

User match = users.stream().filter((user) -> user.getId() < 0).findAny().get();

会抛出一个(好!NoSuchElementException

不过,如果有多个匹配项,我希望它抛出错误。有没有办法做到这一点?


答案 1

创建自定义收集器

public static <T> Collector<T, ?, T> toSingleton() {
    return Collectors.collectingAndThen(
            Collectors.toList(),
            list -> {
                if (list.size() != 1) {
                    throw new IllegalStateException();
                }
                return list.get(0);
            }
    );
}

我们使用 Collectors.collectingAndThen 来构建我们想要的Collector

  1. 收集我们的对象与收集器。ListCollectors.toList()
  2. 在最后应用一个额外的完成器,返回单个元素 — 或抛出 if .IllegalStateExceptionlist.size != 1

用作:

User resultUser = users.stream()
        .filter(user -> user.getId() > 0)
        .collect(toSingleton());

然后,您可以根据需要对此进行自定义,例如,在构造函数中将异常作为参数,将其调整为允许两个值,等等。Collector

另一种 - 可以说不那么优雅 - 解决方案:

您可以使用涉及 和 的“解决方法”,但实际上您不应该使用它。peek()AtomicInteger

相反,您可以做的只是将其收集到 中,如下所示:List

LinkedList<User> users = new LinkedList<>();
users.add(new User(1, "User1"));
users.add(new User(2, "User2"));
users.add(new User(3, "User3"));
List<User> resultUserList = users.stream()
        .filter(user -> user.getId() == 1)
        .collect(Collectors.toList());
if (resultUserList.size() != 1) {
    throw new IllegalStateException();
}
User resultUser = resultUserList.get(0);

答案 2

为了完整起见,以下是@prunge优秀答案对应的“单行”:

User user1 = users.stream()
        .filter(user -> user.getId() == 1)
        .reduce((a, b) -> {
            throw new IllegalStateException("Multiple elements: " + a + ", " + b);
        })
        .get();

这将从流中获取唯一匹配元素,抛出

  • NoSuchElementException如果流为空,或者
  • IllegalStateException如果流包含多个匹配元素。

此方法的变体可避免提前引发异常,而是将结果表示为包含唯一元素,或者如果存在零个或多个元素,则不包含任何内容(空):Optional

Optional<User> user1 = users.stream()
        .filter(user -> user.getId() == 1)
        .collect(Collectors.reducing((a, b) -> null));

推荐