java中if有且只有一个_将Java Stream过滤为1并且只有1个元素

从技术上讲,有一个丑陋的“解决方法”,涉及peek()和一个AtomicInteger ,但你真的不应该使用它。

我在这些情况下所做的只是将其收集在一个列表中,如下所示:

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

我不知道在API中执行此操作的方法,同时我将介绍另一个涉及自定义元素的示例。

更新 ,你应该为此创build自己的Collector :

public static Collector, T> singletonCollector() { return Collector.of( ArrayList::new, List::add, (left, right) -> { left.addAll(right); return left; }, list -> { if (list.size() != 1) { throw new IllegalStateException(); } return list.get(0); } ); }

它所做的是:

它模仿Collectors.toList()收集器。

它在最后应用一个额外的装订器,引发exception,或者如果没有exception,则返回列表的第一个元素。

用作:

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

然后,你可以自定义这个singletonCollector ,只要你想要的,例如在构造函数中给出exception作为参数,调整它以允许两个值,等等。

新的更新 ,我再次修改我的旧回答singletonCollector() ,它可以实际上是这样得到的:

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

哇,这么复杂! :-)其他涉及编写自定义收集器的答案可能更有效(如Louis Wasserman's ,+1),但是如果您想要简洁,build议如下:

List result = users.stream() .filter(user -> user.getId() == 1) .limit(2) .collect(Collectors.toList());

然后validation结果列表的大小。

为了完整起见,这里是与@ prunge的优秀答案相对应的“单行”

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

这从stream中获得唯一的匹配元素,抛出

NoSuchElementException在stream为空的情况下,或者

如果stream包含多个匹配元素,则IllegalStateException 。

这种方法的一个变体避免了提前抛出exception,而是将结果表示为一个包含唯一元素的可选项,如果有零个或多个元素则表示结果为空(空):

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

你可以推出你自己的collections家:

Collector> getOnly() { return Collector.of( AtomicReference::new, (ref, e) -> { if (!ref.compareAndSet(null, e)) { throw new IllegalArgumentException("Multiple values"); } }, (ref1, ref2) -> { if (ref1.get() == null) { return ref2; } else if (ref2.get() != null) { throw new IllegalArgumentException("Multiple values"); } else { return ref1; } }, ref -> Optional.ofNullable(ref.get()), Collector.Characteristics.UNORDERED); }

…或使用您自己的Holdertypes而不是AtomicReference 。 您可以尽可能多地重复使用该Collector 。

(更新:番石榴提供MoreCollectors.onlyElement()在这里做正确的事情)

“逃生孵化”操作可以让你做怪异的事情,而不是stream支持的是要求一个Iterator :

Iterator it = users.stream().filter((user) -> user.getId() < 0).iterator(); if (!it.hasNext()) throw new NoSuchElementException(); else { result = it.next(); if (it.hasNext()) throw new TooManyElementsException(); }

番石榴有一个方便的方法来取一个Iterator并得到唯一的元素,抛出如果有零个或多个元素,这可以取代底部的n-1行。

使用Guava的MoreCollectors.onlyElement() ( JavaDoc )。

它做你想做的,如果stream包含两个或多个元素,则抛出IllegalArgumentException如果stream为空,则抛出NoSuchElementException 。

例:

import static com.google.common.collect.MoreCollectors.onlyElement; User match = users.stream().filter((user) -> user.getId() < 0).collect(onlyElement());

更新(2017-05-30):

这个方法现在是公开的,从Guava 21开始。下面是这个方法的最新的JavaDocs:

更新

好评的@Holgerbuild议:

Optional match = users.stream() .filter((user) -> user.getId() > 1) .reduce((u, v) -> { throw new IllegalStateException("More than one ID found") });

原始答案

Optional#get #get引发exception,但如果有多个元素不起作用。 您可以收集只接受一个项目的集合中的用户,例如:

User match = users.stream().filter((user) -> user.getId() > 1) .collect(toCollection(() -> new ArrayBlockingQueue(1))) .poll();

它抛出一个java.lang.IllegalStateException: Queue full ,但感觉太hacky。

或者你可以使用一个减less结合可选:

User match = Optional.ofNullable(users.stream().filter((user) -> user.getId() > 1) .reduce(null, (u, v) -> { if (u != null && v != null) throw new IllegalStateException("More than one ID found"); else return u == null ? v : u; })).get();

这个减less实质上是返回

如果没有find用户,则返回null

用户,如果只有一个被发现

如果find多个,则会引发exception

结果然后包装在一个可选的。

但最简单的解决scheme可能是收集到一个集合,检查它的大小是1,并得到唯一的元素。

另一种方法是使用reduction(这个例子使用了string,但是可以很容易地应用到任何对象types,包括User )

List list = ImmutableList.of("one", "two", "three", "four", "five", "two"); String match = list.stream().filter("two"::equals).reduce(thereCanBeOnlyOne()).get(); //throws NoSuchElementException if there are no matching elements - "zero" //throws RuntimeException if duplicates are found - "two" //otherwise returns the match - "one" ... //Reduction operator that throws RuntimeException if there are duplicates private static BinaryOperator thereCanBeOnlyOne() { return (a, b) -> {throw new RuntimeException("Duplicate elements found: " + a + " and " + b);}; }

所以对于User的情况下,你会有:

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

我们可以使用RxJava (非常强大的反应扩展库)

LinkedList users = new LinkedList<>(); users.add(new User(1, "User1")); users.add(new User(2, "User2")); users.add(new User(3, "User3")); User userFound = Observable.from(users) .filter((user) -> user.getId() == 1) .single().toBlocking().first();

如果没有用户或多个用户被发现,那么单个 运算符会引发exception。

由于Collectors.toMap(keyMapper, valueMapper)使用Collectors.toMap(keyMapper, valueMapper)合并器来处理多个具有相同键的条目,所以很容易:

List users = new LinkedList<>(); users.add(new User(1, "User1")); users.add(new User(2, "User2")); users.add(new User(3, "User3")); int id = 1; User match = Optional.ofNullable(users.stream() .filter(user -> user.getId() == id) .collect(Collectors.toMap(User::getId, Function.identity())) .get(id)).get();

您将获得重复键的IllegalStateException 。 但最后,我不知道如果代码将不会更易于使用if 。

你有没有尝试过

long c = users.stream().filter((user) -> user.getId() == 1).count(); if(c>1){ throw new IllegalStateException(); }

long count() Returns the count of elements in this stream. This is a special case of a reduction and is equivalent to: return mapToLong(e -> 1L).sum(); This is a terminal operation.

如果您不介意使用第三方库,则Cyclops stream中的 SequenceM (以及简单反应中的 LazyFutureStream )都有单个和单个可选操作符。

如果stream中有0个或多个元素,singleOptional将抛出exception,否则返回单个值。

String result = SequenceM.of("x") .single(); SequenceM.of().single(); // NoSuchElementException SequenceM.of(1,2,3).single(); // NoSuchElementException String result = LazyFutureStream.fromStream(Stream.of("x")) .single();

single可选返回Optional.empty,如果Stream中没有值或多个值。

Optional result = SequenceM.fromStream(Stream.of("x")) .singleOptional(); //Optional["x"] Optional result = SequenceM.of().singleOptional(); // Optional.empty Optional result = SequenceM.of(1,2,3).singleOptional(); // Optional.empty

披露 – 我是两个图书馆的作者。

我正在使用这两个collections家:

public static Collector> zeroOrOne() { return Collectors.reducing((a, b) -> { throw new IllegalStateException("More than one value was returned"); }); } public static Collector onlyOne() { return Collectors.collectingAndThen(zeroOrOne(), Optional::get); }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值