【问题标题】:How to group a set of objects into sorted lists using java 8?java - 如何使用java 8将一组对象分组到排序列表中?
【发布时间】:2015-08-23 20:37:14
【问题描述】:

我想获取一组对象(在这种情况下为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() )));

上面的表达式创建了正确的数据结构:Map,其中键是ObjectInstance 对象的域,值是属性列表的列表。我想要的是现在对列表进行排序,以确保它们按字母顺序排列。有没有办法在同一个表达式中做到这一点?

一个想法是在.stream() 之后添加.sort(),但这真的可以保证工作吗?

【问题讨论】:

    标签: java sorting lambda grouping java-stream


    【解决方案1】:

    使用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()))) )));
    

    您可以提取 Collector 以使代码更具可读性:

    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())));
    

    【讨论】:

      【解决方案2】:

      当然,您可以在收集之前对整个流进行排序:

      Map<String, List<String>> beansByDomain = beans.stream()
              .map(ObjectInstance::getObjectName)
              .sorted(Comparator.comparing(ObjectName::getCanonicalKeyPropertyListString))
              .collect(groupingBy(ObjectName::getDomain,
                                  mapping(ObjectName::getCanonicalKeyPropertyListString,
                                  toList() )));
      

      请注意,我添加了.map(ObjectInstance::getObjectName) 步骤,因为您不需要来自ObjectInstance 的任何其他内容。这会很好地工作,但我无法预测它是否比单独排序每个结果列表更快。

      如果您更喜欢单独的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;});
      }
      

      这里我们明确收集到ArrayListtoList() 也这样做,但不能保证),然后在不额外复制的情况下对结果列表进行就地排序(使用stream().sorted().collect(toList()) 你复制整个列表内容至少两次)。另请注意,&lt;T&gt; 参数必须声明为extends Comparable&lt;T&gt;。否则你可能会错误地将这个收集器用于不可比较的类型,这会编译得很好,但会导致运行时错误。

      【讨论】:

      • 我也喜欢这个解决方案。我应该考虑到 ObjectName 的映射,它让一切变得更干净。昨天是漫长的一天……
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-10-05
      • 1970-01-01
      • 1970-01-01
      • 2022-10-17
      • 2020-05-20
      • 1970-01-01
      • 2022-01-15
      相关资源
      最近更新 更多