Java Streams 验证是否只有一个结果

问题描述 投票: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 streams api 来缩短验证时间?

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

坦克!

java lambda stream
3个回答
1
投票

Guava 库有收集器

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.