使用 Java 流验证是否只有一个结果

问题描述 投票:0回答:3

通常,使用列表我会遇到如下情况(假设用户包含 ID 和名称):

List<User> users = loadFromDb();
List<User> lookingForIdResultList = user.stream()
.filter( u -> u.getId() == 1234)
.collect(Collectors.toList())

if(lookingForIdResultList.size() == 0) throw UserNotFoundException()
if(lookingForIdResultList.size() > 1) throw MultipleIdsNotPermittedExcpetion()

User searchedUser = lookingForIdResultList.get(0);
System.out.println("Username is "+ searchedUser.getName())

有没有办法使用 Java Stream API 来缩短验证时间?

这只是一个过于简单的例子,问题是普遍的。我不想从数据库加载用户。

java java-stream
3个回答
2
投票

Guava 库有 Collectors

toOptional()
onlyElement()
正是针对此用例:

// throws exception if there is not exactly 1 matching user
User searchedUser = users.stream()
    .filter(u -> u.getId() == 1234)
    .collect(MoreCollectors.onlyElement());

https://www.javadoc.io/doc/com.google.guava/guava/28.0-jre/com/google/common/collect/MoreCollectors.html#onlyElement--


0
投票

如果您想在代码中进行验证,那么它不会变得更短,但您可以使代码更具可读性。

一个小的优化是使用

count()
而不是
collect()
:

users.stream().filter(...).count();

一般来说,我会构建一些辅助函数来完成这项工作,这样你的代码就会看起来更干净、更具可读性:

List<User> users = loadFromDb();
validateUsers(users); // throws exception

如果您在多个地方需要类似的功能,则可以进一步抽象

validateUsers
方法:

static void validateUsers(List<User> users) {
  validateListHasExactlyOne(users, user -> user.getId() == 1234);
}

static <T> void validateListHasExactlyOne(List<T> list, Predicate<T> pred) {
  long count = list.stream().filter(pred).count();
  if(count == 0) throw ...
  if(count > 1) throw ...
}

0
投票

请注意,流可能并不总是最好的选择。普通的旧 for 循环可能更容易阅读和编写,而且速度也更快(至少如果您不使用

parallelStream()
)。

static <T> T exactMatch(List<T> list, Predicate<T> predicate) {
  T result = null;
  for(T element : list) {
    if( predicate.test(element) ) {
      if(result != null) throw new DuplicateException(); //or some other exception 
      result = element;
    }
  }
  if( result == null ) throw new NoSuchElementException();
  return result;
}

这看起来更长,但它有一些优点:

  • 它仅使用一次迭代来检查重复项并返回单个结果(对流中的结果进行计数,然后获取一个结果需要 2 次迭代)
  • 不需要中间列表,即不需要额外的内存

但有一个缺点:它是单线程的。如果需要并行化,您可能最好使用

parallelStream()

如果您还需要能够指定异常,您可以尝试类似的操作(特定异常要么未经检查,要么从基本异常继承):

static <T> T exactMatch(List<T> list, Predicate<T> predicate, 
                        Supplier<D extends RuntimeException> duplicateSupplier, 
                        Supplier<N extends NoSuchElementException> noElementSupplier) {

   //same as above but using "throw duplicateSupplier.get()" etc.
}

您还可以进行重载以使用标准异常,例如

static <T> T exactMatch(List<T> list, Predicate<T> predicate) {
  return exactMatch(list, predicate, DuplicateException::new, NoSuchElementException::new);
}
© www.soinside.com 2019 - 2024. All rights reserved.