我有一个包含以下元素的数组列表:
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"));
记录是一个简单的POJO,其ID和名称为
我想在列表中执行这些操作。
创建一个像Map<Integer, List<Record>>
这样的地图,其中一个密钥是id,并且更加简洁的密钥添加为列表。我已经完成了下面的操作。
Map<Integer, List<Record>> map = list.stream()
.collect(Collectors.groupingBy(Record::getId, HashMap::new, Collectors.toList()));
现在我想按名称和子列表对列表进行排序,以提供地图内的限制
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()))));
我上面尝试过,看起来这不是一个好方法。任何人都可以提出更好的方法吗?
答案 0 :(得分:11)
您可以使用Java 8 Collectors.collectingAndThen()
方法:
@EnableWs
答案 1 :(得分:9)
您可以使用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()));
})));
此解决方案避免为每个列表组创建新流。
正如在this answer中指出的那样,通过使用Collectors.toCollection(ArrayList::new)
,我们确保列表是可变的,以便我们以后可以对其进行排序。
答案 2 :(得分:5)
您可以使用
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();
});
使用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;
})));
作为奖励,我还添加了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)));
答案 3 :(得分:4)
list.stream()
.collect(Collectors.groupingBy(
Record::getId,
Collectors.collectingAndThen(
Collectors.toList(),
x -> x.stream()
.sorted(Comparator.comparing(Record::getName))
.limit(limit)
.collect(Collectors.toList()))));
答案 4 :(得分:1)
您可以在收集地图中的项目之前对进行排序。对于限制位,您可以使用2-0.txt
对列表进行后处理,并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()))));
,结果为
limit = 2