2017-06-22 74 views
0

是否可以对Stream中的元素进行分组,然后继续流式传输而不必从返回的地图的EntrySet创建新的流?是否可以在不关闭流的情况下对元素进行分组?

例如,我可以这样做:

public static void main(String[] args) { 
    // map of access date to list of users 
    // Person is a POJO with first name, last name, etc. 
    Map<Date, List<Person>> dateMap = new HashMap<>(); 
    // ... 
    // output, sorted by access date, then person last name 
    dateMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(e -> { 
     Date date = e.getKey(); 
     // group persons by last name and sort 
     // this part seems clunky 
     e.getValue().stream().collect(Collectors.groupingBy(Person::getLastName, Collectors.toSet())) 
       .entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(e2 -> { 
      // pool agent id is the key 
      String lastName = e2.getKey(); 
      Set<Person> personSet = e2.getValue(); 
      float avgAge = calculateAverageAge(personSet); 
      int numPersons = personSet.size(); 
      // write out row with date, lastName, avgAge, numPersons 
     }); 
    }); 
} 

这工作得很好,但似乎有点麻烦,尤其是流进一个地图,然后立即进入集映射的流。

有没有办法将流中的对象分组,但继续流式传输?

+2

简单的答案是*不*;但可能你可以准确解释你想要达到的目标(输入和输出),我们可以帮助解决这个问题? – Eugene

+0

@Eugene谢谢。这个问题恰恰包含了我想达到的目标,或多或少。我有dateMap的工作,我需要输出行到按日期,然后姓氏,按日期或姓氏排序的报告。 – lucasvw

+1

在你的问题中没有任何明显的。我可以推论的是,你想按日期订购人员,然后按姓氏。这种排序是否足够,还是你真的需要将它们分组? – VGR

回答

1

您可以使用Map.forEach,下游收集器,TreeMap和IntSummaryStatistics缩短代码。

通过分组到TreeMap(而不是将其保留到groupingBy收集器),您可以自动排序名称。您不需要立即获取分组地图,而是添加一个summarizingInt收集器,将具有相同名称的人员列表变成他们年龄的IntSummaryStatistics

public static void main(String[] args) { 
    Map<Date, List<Person>> dateMap = new HashMap<>(); 
    dateMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(e -> { 
     Date date = e.getKey(); 

     e.getValue().stream() 
        .collect(Collectors.groupingBy(Person::getLastName, 
                TreeMap::new, 
                Collectors.summarizingInt(Person::getAge))) 
        .forEach((name, stats) -> System.out.println(date +" "+ 
                   lastName +" "+ 
                   stats.getAverage() +" "+ 
                   stats.getCount())); 
    }); 
} 

如果你有超过初始地图类型控制,您可以使用TreeMap中有作为,并进一步缩短:

public static void main(String[] args) { 
    Map<Date, List<Person>> dateMap = new TreeMap<>(); 
    dateMap.forEach((date, persons -> { ... 
+4

我知道,使用'TreeMap'来诱使地图已经在'groupingBy'集合之后排序,但是与第一个直觉相反,收集到默认(散列)地图和事后排序在大多数情况下会更有效案例。 – Holger

+0

@霍尔这是我的想法。谢谢你,马尔特! – lucasvw

+0

@Holger感谢您指出。我认为那里可能有所不同,但希望缩短代码。卢卡斯,如果性能影响值得关注,您仍然可以使用下游收集器汇总人员,并用'entrySet.stream.sorted.foreach'替换'forEach'。它和你的代码几乎一样,但至少平均值已经计算出来了。 Holger,你有一个想法如何解决物化分组地图? –

相关问题