java 如何使用java 8将一组对象分组到排序列表中?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32171283/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to group a set of objects into sorted lists using java 8?
提问by Ken DeLong
I want to take a set of objects (ObjectInstance
in this case), and I want to group them by one property, and have the resulting lists be sorted on another.
我想获取一组对象(ObjectInstance
在这种情况下),我想按一个属性对它们进行分组,并将结果列表按另一个属性排序。
Set<ObjectInstance> beans = server.queryMBeans(null, null);
Map<String, List<String>> beansByDomain = beans.stream()
.collect(groupingBy( (ObjectInstance oi) -> oi.getObjectName().getDomain(),
mapping((ObjectInstance oi) -> oi.getObjectName().getCanonicalKeyPropertyListString(),
toList() )));
The above expression creates the correct data structure: a Map
where the keys are the domains of the ObjectInstance
objects, and the values are Lists of the property lists. What I want is to now sort the Lists, to make sure they are in alphabetical order. Is there some way to do this in the same expression?
上面的表达式创建了正确的数据结构: aMap
其中键是ObjectInstance
对象的域,值是属性列表的列表。我现在想要的是对列表进行排序,以确保它们按字母顺序排列。有没有办法在同一个表达式中做到这一点?
One idea would be to add .sort()
right after .stream()
, but is that really guaranteed to work?
一个想法是添加.sort()
之后.stream()
,但真正保证工作?
回答by Jean Logeart
Use collectingAndThen
:
List<String> beansByDomain = beans.stream()
.collect(groupingBy( (ObjectInstance oi) -> oi.getObjectName().getDomain(),
mapping((ObjectInstance oi) -> oi.getObjectName().getCanonicalKeyPropertyListString(),
collectingAndThen(toList(), (l -> l.stream().sorted().collect(toList()))) )));
You can extract the Collector to make the code more readable:
您可以提取收集器以使代码更具可读性:
public static <T> Collector<T,?,List<T>> toSortedList() {
return Collectors.collectingAndThen(Collectors.toList(),
l -> l.stream().sorted().collect(toList()));
}
List<String> beansByDomain = beans.stream()
.collect(groupingBy( (ObjectInstance oi) -> oi.getObjectName().getDomain(),
mapping((ObjectInstance oi) -> oi.getObjectName().getCanonicalKeyPropertyListString(),
toSortedList())));
回答by Tagir Valeev
Surely you can sort the whole stream before collecting:
当然,您可以在收集之前对整个流进行排序:
Map<String, List<String>> beansByDomain = beans.stream()
.map(ObjectInstance::getObjectName)
.sorted(Comparator.comparing(ObjectName::getCanonicalKeyPropertyListString))
.collect(groupingBy(ObjectName::getDomain,
mapping(ObjectName::getCanonicalKeyPropertyListString,
toList() )));
Note that I added the .map(ObjectInstance::getObjectName)
step as you don't need anything else from ObjectInstance
. This will work nicely, though I cannot predict whether it's faster than sorting each resulting list separately or not.
请注意,我添加了该.map(ObjectInstance::getObjectName)
步骤,因为您不需要来自ObjectInstance
. 这会很好地工作,但我无法预测它是否比单独对每个结果列表进行排序更快。
If you prefer the separate toSortingList()
collector (as in @JeanLogeart answer), it can be optimized like this:
如果您更喜欢单独的toSortingList()
收集器(如@JeanLogeart 的回答),可以这样优化:
public static <T extends Comparable<T>> Collector<T,?,List<T>> toSortedList() {
return collectingAndThen(toCollection(ArrayList::new),
(List<T> l) -> {l.sort(Comparator.naturalOrder()); return l;});
}
Here we explicitly collect to ArrayList
(toList()
does the same, but it's not guaranteed), then sort the resulting list in-place without additional copying (using stream().sorted().collect(toList())
you copy the whole list content at least twice). Also note that <T>
parameter must be declared as extends Comparable<T>
. Otherwise you can mistakenly use this collector for non-comparable type which would compile fine, but result in runtime error.
在这里,我们明确收集到ArrayList
(toList()
执行相同的操作,但不能保证),然后就地对结果列表进行排序,无需额外复制(使用stream().sorted().collect(toList())
您复制整个列表内容至少两次)。另请注意,<T>
参数必须声明为extends Comparable<T>
. 否则,您可能会错误地将此收集器用于非可比较类型,该类型可以很好地编译,但会导致运行时错误。