英文:
java 11 lambda, aggregate by same property and the filter with a certain condition
问题
以下是翻译好的部分:
我有以下实体:
ID | PROPERTY
FOO | A
FOO | Z
JOHN | A
DOE | Z
现在我想按ID对这些实体进行分组,并且如果一个或两个值都存在,则取具有属性'A'的值,否则取'Z'。
因此输出应为:
FOO | A
JOHN | A
DOE | Z
我考虑使用groupingBy流方法,但我不知道要使用哪些收集器,也不知道这是否是正确的解决方案:
list.stream()
.collect(Collectors.groupingBy(o -> o.getId(), ? 另一个收集器函数 ?)
.collect(Collectors.toList()));
英文:
I have the following entities:
ID | PROPERTY
FOO | A
FOO | Z
JOHN | A
DOE | Z
Now I want to group these entities by ID and the take the one with property 'A' if one or both valuesare present , otherwise Z.
Thus the output should be:
FOO | A
JOHN | A
DOE | Z
I was thinking about the groupingBy stream method, but I don't know how what collectors to use neither if is the correct solution:
list.stream()
.collect(Collectors.groupingBy(o -> o.getId(), ? another collector function ?)
.collect(Collectors.toList()));
答案1
得分: 1
试一试:
entities.stream()
.collect(toMap(YourEntityType::getId,
YourEntityType::getProperty,
(x, y) -> "A".equals(x) ? x : y));
更新:
我刚注意到在我编辑答案的时候,@Holger 在他的评论中发表了完全相同的内容。不过有两个小区别:
- 我更喜欢使用方法引用而不是 lambda 表达式(这是一种品味问题,但我觉得它们比 lambda 表达式更不容易出错)
"A".equals(x)
比x.equals("A")
更安全。后者在x
为null
的情况下会引发 NPE(空指针异常)。
更新 2:
既然你想要以列表的形式获得结果,那么这个选项应该适合你:
entities.stream()
.collect(collectingAndThen(
toMap(YourEntityType::getId,
identity(),
(x, y) -> "A".equals(x.getProperty()) ? x : y,
m -> new ArrayList<YourEntityType>(m.values()));
英文:
Try this:
entities.stream()
.collect(toMap(YourEntityType::getId,
YourEntityType::getProperty,
(x, y) -> "A".equals(x) ? x : y));
Update:
I've just noticed that @Holger posted exactly the same thing in his comment, while I was editing my answer. There are two minor differences though:
- I prefer using method references instead of lambdas (It's a matter of taste, but I find them less error prone than lambdas)
"A".equals(x)
is safer thanx.equals("A")
. The latter will fail with NPE in casex
isnull
.
Update 2:
Since you want the result in form of a list, then this option should work for you:
entities.stream()
.collect(collectingAndThen(
toMap(YourEntityType::getId,
identity(),
(x, y) -> "A".equals(x.getProperty()) ? x : y,
m -> new ArrayList<YourEntityType>(m.values()));
答案2
得分: 1
如果您坚持使用分组收集器,`? 另一个收集器函数 ?` 的代码如下所示:
```java
public static void main(String[] args) {
record C(String id, String property) {}
List<C> list = List.of(new C("FOO","A"),new C("FOO","Z"),new C("JOHN","A"),new C("DOE","Z"),new C("XXX","X"));
System.out.println(list);
Object result = list.stream()
.collect(Collectors.groupingBy(o -> o.id, Collectors.collectingAndThen(Collectors.reducing((l,r) -> "A".equals(l.property) ? l : r), Optional::get)));
System.out.println(result);
}
这是IDEA建议的几乎与 @ETO 的解决方案相近的修正方式。
(抱歉,由于有记录,我懒得使用 getters。)
<details>
<summary>英文:</summary>
If you insisted on using grouping collector, the `? another collector function ?` would look like this:
```java
public static void main(String[] args) {
record C(String id, String property) {}
List<C> list = List.of(new C("FOO","A"),new C("FOO","Z"),new C("JOHN","A"),new C("DOE","Z"),new C("XXX","X"));
System.out.println(list);
Object result = list.stream()
.collect(Collectors.groupingBy(o -> o.id, Collectors.collectingAndThen(Collectors.reducing((l,r) -> "A".equals(l.property) ? l : r), Optional::get)));
System.out.println(result);
}
, which Idea suggest to correct to almost @ETO's solution.
(Apologize, having records I am lazy to use getters.)
答案3
得分: 1
// 具有可重用方法的实用程序类,您也可以将其用于其他目的
public final class StreamUtil {
private StreamUtil() {}
public static <T, U> Predicate<T> distinctBy(Function<T, U> keyFunction) {
final Set<U> uniqueKeys = synchronizedSet(new HashSet<>());
return item -> uniqueKeys.add(keyFunction.apply(item));
}
}
和您问题的解决方案
entities.stream()
.sorting(Comparator.comparing(Entity::getProperty)) // 根据优先级对实体进行排序
.filter(StreamUtil.distinctBy(Entity::getId)) // 只获取每个键的第一个实体
... // 在这里,您可以根据您的要求继续使用流。您可以进行collect()操作或继续进行映射/过滤操作
这个解决方案比其他建议的解决方案慢,因为涉及排序。
然而,它也有它的优点——更容易阅读,慢速性能只会在大集合上表现出来。
英文:
// Utility class with methods you can reuse for other purposes too
public final class StreamUtil {
private StreamUtil() {}
public static <T, U> Predicate<T> distinctBy(Function<T, U> keyFunction) {
final Set<U> uniqueKeys = synchronizedSet(new HashSet<>());
return item -> uniqueKeys.add(keyFunction.apply(item));
}
}
and solution for your question
entities.stream()
.sorting(Comparator.comparing(Entity::getProperty)) // order entities according to priorities
.filter(StreamUtil.distinctBy(Entity::getId)) // take first entity only per key
... // here you have stream as per you requirements. You can collect() it or continue mapping/filtering
This solution is slower than other suggested here because of sorting.
However, it has its advantage - easier to read, while slowness will kick in on large collections only.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论