Pra*_*ath 16 java lambda java-8
我有一个包含以下元素的数组列表:
List<Record> list = new ArrayList<>();
list.add(new Record(3, "32"));
list.add(new Record(4, "42"));
list.add(new Record(1, "12"));
list.add(new Record(1, "11"));
list.add(new Record(2, "22"));
list.add(new Record(5, "52"));
list.add(new Record(5, "53"));
list.add(new Record(5, "51"));
Run Code Online (Sandbox Code Playgroud)
Record是一个简单的POJO,它有id和name
我想在列表中做那些.
创建一个像Map<Integer, List<Record>>这样的地图有一个密钥是id和更细的密钥添加为列表.我已经做了如下.
Map<Integer, List<Record>> map = list.stream()
.collect(Collectors.groupingBy(Record::getId, HashMap::new, Collectors.toList()));
Run Code Online (Sandbox Code Playgroud)现在我想按名称和子列表对列表进行排序,以提供内部限制
map.forEach((k, v) -> v.stream().sorted(Comparator.comparing(Record::getName)));
map.forEach((k, v) -> map.put(k, v.subList(0, Math.min(**limit**, v.size()))));
Run Code Online (Sandbox Code Playgroud)我已经尝试过,看起来这不是一个好方法.有谁能建议更好的方法?
Ole*_*hov 11
您可以使用Java 8 Collectors.collectingAndThen()方法:
Map<Integer, List<Record>> map = list.stream()
.collect(Collectors.groupingBy(
Record::getId,
Collectors.collectingAndThen(
Collectors.toList(),
records -> records.stream()
.sorted(Comparator.comparing(Record::getName))
.limit(limit)
.collect(Collectors.toList()))));
Run Code Online (Sandbox Code Playgroud)
你可以使用Collectors.collectingAndThen:
Map<Integer, List<Record>> result = list.stream()
.collect(Collectors.groupingBy(
Record::getId,
Collectors.collectingAndThen(
Collectors.toCollection(ArrayList::new),
v -> {
v.sort(Comparator.comparing(Record::getName));
return v.subList(0, Math.min(LIMIT, v.size()));
})));
Run Code Online (Sandbox Code Playgroud)
此解决方案避免为每个列表组创建新流.
正如在这个答案中指出的那样,通过使用Collectors.toCollection(ArrayList::new)我们确保列表是可变的,以便我们以后可以对它进行排序.
您可以使用
Map<Integer, List<Record>> map = list.stream()
.collect(Collectors.groupingBy(Record::getId,Collectors.toCollection(ArrayList::new)));
map.values().forEach(l -> {
list.sort(Comparator.comparing(Record::getName));
l.subList(limit, l.size()).clear();
});
Run Code Online (Sandbox Code Playgroud)
使用Collectors.toCollection(ArrayList::new)我们确保结果列表是可变的.然后我们就地对列表进行排序并删除不必要的值.我们不是构建包含我们想要的元素的子列表(它将保留对完整列表的引用),而是构建我们不想要的元素的子列表clear(),以便有效地从原始列表中删除这些元素.
您也可以将其写为单个语句:
Map<Integer, List<Record>> map = list.stream()
.collect(Collectors.groupingBy(Record::getId,
Collectors.collectingAndThen(
Collectors.toCollection(ArrayList::new),
l -> {
list.sort(Comparator.comparing(Record::getName));
l.subList(limit, l.size()).clear();
l.trimToSize();
return l;
})));
Run Code Online (Sandbox Code Playgroud)
作为奖励,我还添加l.trimToSize();了指示ArrayList使用较小的数组,如果前面.subList(limit, l.size()).clear()删除了很多元素.由于这可能意味着复制操作,因此这是CPU时间和内存之间的权衡.因此,如果结果仅在之后的相当短的时间内使用,则不会使用trimToSize().
使用StreamEx时,操作变得更简单(并且可能更有效):
Map<Integer, List<Record>> map = list.stream()
.collect(Collectors.groupingBy(Record::getId,
MoreCollectors.least(Comparator.comparing(Record::getName), limit)));
Run Code Online (Sandbox Code Playgroud)
您可以在收集地图中的项目之前进行排序。对于限制位,您可以使用collectingAndThen后处理列表及其stream.limit。
Map<Integer, List<Record>> map = list.stream()
.sorted(Comparator.comparing(Record::getName))
.collect(Collectors.groupingBy(Record::getId,
Collectors.collectingAndThen(Collectors.toList(),
l -> l.stream().limit(limit).collect(Collectors.toList()))));
Run Code Online (Sandbox Code Playgroud)
limit = 2,这导致
{1=[Record(id=1, name=11), Record(id=1, name=12)],
2=[Record(id=2, name=22)],
3=[Record(id=3, name=32)],
4=[Record(id=4, name=42)],
5=[Record(id=5, name=51), Record(id=5, name=52)]}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1043 次 |
| 最近记录: |