ryv*_*age 202 java lambda java-8 java-stream
我正在尝试使用Java 8 Stream
来查找a中的元素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());
}
Run Code Online (Sandbox Code Playgroud)
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;
}
}
Run Code Online (Sandbox Code Playgroud)
此代码User
根据其ID找到.但是无法保证有多少User
匹配过滤器.
将过滤线更改为:
User match = users.stream().filter((user) -> user.getId() < 0).findAny().get();
Run Code Online (Sandbox Code Playgroud)
会扔一个NoSuchElementException
(好!)
如果有多个匹配,我希望它抛出一个错误.有没有办法做到这一点?
ski*_*iwi 170
Collector
public static <T> Collector<T, ?, T> toSingleton() {
return Collectors.collectingAndThen(
Collectors.toList(),
list -> {
if (list.size() != 1) {
throw new IllegalStateException();
}
return list.get(0);
}
);
}
Run Code Online (Sandbox Code Playgroud)
我们Collectors.collectingAndThen
用来构建我们想要Collector
的
List
与Collectors.toList()
收藏家.IllegalStateException
if list.size != 1
.用作:
User resultUser = users.stream()
.filter(user -> user.getId() > 0)
.collect(toSingleton());
Run Code Online (Sandbox Code Playgroud)
然后,您可以根据需要自定义此项Collector
,例如,在构造函数中将异常作为参数提供,调整它以允许两个值,等等.
您可以使用涉及peek()
和的"解决方法" AtomicInteger
,但实际上您不应该使用它.
你能做什么istead只是收集它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);
Run Code Online (Sandbox Code Playgroud)
glt*_*lts 102
为了完整起见,这里是@ one-liner'对应@ prunge的优秀答案:
User user1 = users.stream()
.filter(user -> user.getId() == 1)
.reduce((a, b) -> {
throw new IllegalStateException("Multiple elements: " + a + ", " + b);
})
.get();
Run Code Online (Sandbox Code Playgroud)
这从流中获取唯一匹配元素,抛出
NoSuchElementException
如果流是空的,或者IllegalStateException
如果流包含多个匹配元素.这种方法的一种变体可以避免提前抛出异常,而是将结果表示为Optional
包含唯一元素,或者如果存在零个或多个元素则表示无结果(空):
Optional<User> user1 = users.stream()
.filter(user -> user.getId() == 1)
.collect(Collectors.reducing((a, b) -> null));
Run Code Online (Sandbox Code Playgroud)
Stu*_*rks 80
其他涉及编写习惯的答案Collector
可能更有效(例如Louis Wasserman's,+ 1),但如果你想要简洁,我建议如下:
List<User> result = users.stream()
.filter(user -> user.getId() == 1)
.limit(2)
.collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)
然后验证结果列表的大小.
Lou*_*man 63
番石榴提供MoreCollectors.onlyElement()
了正确的东西.但如果你必须自己动手,你可以自己动手Collector
:
<E> Collector<E, ?, Optional<E>> 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);
}
Run Code Online (Sandbox Code Playgroud)
...或使用您自己的Holder
类型而不是AtomicReference
.您可以根据需要重复使用它Collector
.
小智 41
使用Guava MoreCollectors.onlyElement()
(JavaDoc).
IllegalArgumentException
如果流包含两个或更多元素,它会执行您想要的操作并抛出一个,NoSuchElementException
如果流是空的,则抛出一个.
import static com.google.common.collect.MoreCollectors.onlyElement;
User match =
users.stream().filter((user) -> user.getId() < 0).collect(onlyElement());
Run Code Online (Sandbox Code Playgroud)
Bri*_*etz 30
"逃生舱"操作可以让你做一些流不支持的怪异事情,就是要求Iterator
:
Iterator<T> 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();
}
Run Code Online (Sandbox Code Playgroud)
番石榴有一种方便的方法来获取Iterator
并获得唯一的元素,如果有零个或多个元素则抛出,这可以替换这里的底部n-1行.
ass*_*ias 21
来自@Holger的评论中的好建议:
Optional<User> match = users.stream()
.filter((user) -> user.getId() > 1)
.reduce((u, v) -> { throw new IllegalStateException("More than one ID found") });
Run Code Online (Sandbox Code Playgroud)
抛出异常Optional#get
,但是如果你有多个元素无济于事.您可以收集仅接受一个项目的集合中的用户,例如:
User match = users.stream().filter((user) -> user.getId() > 1)
.collect(toCollection(() -> new ArrayBlockingQueue<User>(1)))
.poll();
Run Code Online (Sandbox Code Playgroud)
抛出一个java.lang.IllegalStateException: Queue full
,但感觉太hacky.
或者你可以使用减少与可选:
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();
Run Code Online (Sandbox Code Playgroud)
减少基本上返回:
然后将结果包装在一个可选项中.
但最简单的解决方案可能只是收集到一个集合,检查它的大小是1并获得唯一的元素.
小智 17
我认为这种方式更简单:
User resultUser = users.stream()
.filter(user -> user.getId() > 0)
.findFirst().get();
Run Code Online (Sandbox Code Playgroud)
Fab*_*nte 13
这是我发现的更简单灵活的方法(基于@prunge 答案)
Optional<User> user = users.stream()
.filter(user -> user.getId() == 1)
.reduce((a, b) -> {
throw new IllegalStateException("Multiple elements: " + a + ", " + b);
})
Run Code Online (Sandbox Code Playgroud)
通过这种方式您可以获得:
Optional.empty()
如果不存在pru*_*nge 12
另一种方法是使用简化:(此示例使用字符串,但可以轻松应用于任何对象类型,包括User
)
List<String> 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 <T> BinaryOperator<T> thereCanBeOnlyOne()
{
return (a, b) -> {throw new RuntimeException("Duplicate elements found: " + a + " and " + b);};
}
Run Code Online (Sandbox Code Playgroud)
因此,对于User
您将拥有的情况:
User match = users.stream().filter((user) -> user.getId() < 0).reduce(thereCanBeOnlyOne()).get();
Run Code Online (Sandbox Code Playgroud)
Collector
:public static <T> Collector<T, ?, Optional<T>> toSingleton() {
return Collectors.collectingAndThen(
Collectors.toList(),
list -> list.size() == 1 ? Optional.of(list.get(0)) : Optional.empty()
);
}
Run Code Online (Sandbox Code Playgroud)
Optional<User> result = users.stream()
.filter((user) -> user.getId() < 0)
.collect(toSingleton());
Run Code Online (Sandbox Code Playgroud)
我们返回一个Optional
,因为我们通常不能假设它只Collection
包含一个元素.如果您已经知道这种情况,请致电:
User user = result.orElseThrow();
Run Code Online (Sandbox Code Playgroud)
这会给错误的人带来错误的负担 - 正如它应该的那样.
小智 5
使用Reduce和Optional
法比奥·邦凡特(Fabio Bonfante)回复:
public <T> T getOneExample(Collection<T> collection) {
return collection.stream()
.filter(x -> /* do some filter */)
.reduce((x,y)-> {throw new IllegalStateException("multiple");})
.orElseThrow(() -> new NoSuchElementException("none"));
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
161299 次 |
最近记录: |