首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Collectors.groupingBy进入对象列表?

Collectors.groupingBy进入对象列表?
EN

Stack Overflow用户
提问于 2016-01-24 12:16:06
回答 2查看 9.7K关注 0票数 4

给出一个TimeEntry对象的列表,我想将它们分组为Day和Hour对象。

其中,Day有小时对象列表,而Hour有它所代表的小时的TimeEntry对象列表。

日和小时还保存在各自列表中的TimeEntry持续时间之和。如果可能的话,也要把当天的名字写在“日”里。TimeEntry的列表按“开始”排序。

代码语言:javascript
复制
public class TimeEntry {
  private LocalDateTime start;
  private Duration duration;

  public Integer getDay() { return this.start.get(ChronoField.DAY_OF_MONTH); }
  public Integer getHour() { return this.start.get(ChronoField.HOUR_OF_DAY); }
  public String getDayName() {
        return this.start.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.getDefault());
    }
}

public class Day {
  private Integer dayNr;
  private String dayName;
  private Duration sumOfHours;
  private List<Hour> hours;
}

public class Hour {
  private Integer hourNr;
  private Duration sumOfEntries;
  private List<TimeEntry> entries;
}

如何使用Java 8S的流来实现这一点?

代码语言:javascript
复制
List<Day> days = timeEntries.stream().collect(Collectors.groupingBy(...? 

样本输入:

代码语言:javascript
复制
List<TimeEntry> timeEntries = [{"2016-01-22 10:00", "5 minutes"}, 
{"2016-01-23 08:00", "7 minutes"} , {"2016-01-23 08:43", "3 minutes"}]

样本输出:

代码语言:javascript
复制
[
{dayNr: 22, dayName: Friday, sumofEntries: 5 minutes
  [{hourNr: 10, sumofEntries: 5 minutes}, 
    [{"2016-01-22 10:00", "5 minutes"}]} ]},
{dayNr: 23, dayName: Saturday, sumofEntries: 10 minutes
  [{hourNr: 8, sumofEntries: 10 minutes}, 
    [{"2016-01-23 08:00", "7 minutes"},
     {"2016-01-23 08:43", "3 minutes"} ]}
]
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-01-24 18:05:10

这是一个相当复杂的操作。直接的方法是首先通过几天的分组来创建一个Map<Integer, Map<Integer, List<TimeEntry>>>,然后再分几个小时。一旦我们有了这个映射,我们就可以对它进行后处理,以创建想要的List<Day>

创建临时地图非常容易。我们可以使用Collectors.groupingBy(classifier, downstream),其中分类器返回日期(通过方法引用TimeEntry::getDay),下游收集器是另一个按小时进行分类的groupingBy收集器(通过方法引用TimeEntry::getHour)。在这个步骤之后,我们每天都有一个映射,其中值是映射到对应的时间条目的每个小时的映射。

接下来,我们需要做的是利用这个地图制作一个List<Day>。基本上,映射的每个条目(所以每个天数)都必须映射到相应的Day对象。

  • dayNr只是条目的键。
  • dayName是该日和一小时的时间条目之一的日期名称。一次输入必须存在,因为我们之前按这些字段分组:为了检索它,我们获得了该天号的小时地图Map<Integer, List<TimeEntry>>,并且只保留第一个值。
  • 可以通过操作该天号的小时地图Map<Integer, List<TimeEntry>>来检索该Map<Integer, List<TimeEntry>>。对于该地图的每个条目:
    • hourNr只是条目的键。
    • entries字段是条目的值。
    • sumOfEntries是该小时数的时间条目的所有持续时间的加法。

  • 最后,sumOfHours是对该天数的每个小时数的所有sumOfEntries的加法。

一个完整的实现如下所示,其中假定所有域对象都有一个适当的构造函数和适当的getter:

代码语言:javascript
复制
public static void main(String[] args) {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
    List<TimeEntry> timeEntries = Arrays.asList(
        new TimeEntry(LocalDateTime.parse("2016-01-22 10:00", formatter), Duration.ofMinutes(5)),
        new TimeEntry(LocalDateTime.parse("2016-01-23 08:00", formatter), Duration.ofMinutes(7)),
        new TimeEntry(LocalDateTime.parse("2016-01-23 08:43", formatter), Duration.ofMinutes(3))
    );

    Map<Integer, Map<Integer, List<TimeEntry>>> map = 
        timeEntries.stream()
                   .collect(groupingBy(TimeEntry::getDay, groupingBy(TimeEntry::getHour)));

    List<Day> output =
        map.entrySet()
           .stream()
           .map(e -> {
              String dayName = e.getValue().values().iterator().next().get(0).getDayName();
              List<Hour> hours =
                 e.getValue().entrySet()
                             .stream()
                             .map(he -> new Hour(he.getKey(), sumDuration(he.getValue(), TimeEntry::getDuration), he.getValue()))
                             .collect(toList());
              return new Day(e.getKey(), dayName, sumDuration(hours, Hour::getSumOfEntries), hours);
           })
           .collect(toList());

    System.out.println(output);
}

private static <T> Duration sumDuration(List<T> list, Function<T, Duration> function) {
    return list.stream().map(function::apply).reduce(Duration.ofMinutes(0), (d1, d2) -> d1.plus(d2));
}

请注意,将列表中的Duration对象添加到帮助方法sumDuration中。

上面示例的输出(假设toString()打印了方括号中的所有字段):

代码语言:javascript
复制
[Day [dayNr=22, dayName=vendredi, sumOfHours=PT5M, hours=[Hour [hourNr=10, sumOfEntries=PT5M, entries=[TimeEntry [start=2016-01-22T10:00, duration=PT5M]]]]], Day [dayNr=23, dayName=samedi, sumOfHours=PT10M, hours=[Hour [hourNr=8, sumOfEntries=PT10M, entries=[TimeEntry [start=2016-01-23T08:00, duration=PT7M], TimeEntry [start=2016-01-23T08:43, duration=PT3M]]]]]]
票数 2
EN

Stack Overflow用户

发布于 2016-01-24 17:47:03

嗯,我不能为你写所有的代码,但这将实现蜘蛛的想法,希望能让你开始。在像这样实例化TimeEntry列表之后(参见how to initialize static ArrayList in one line):

代码语言:javascript
复制
List<TimeEntry> timeEntries = new ArrayList<TimeEntry>() {
    {
        add(new TimeEntry("2016-01-22T10:00", "PT5M"));
        add(new TimeEntry("2016-01-23T08:00", "PT7M"));
        add(new TimeEntry("2016-01-23T08:43", "PT3M"));
    }
};

那你就stream吧。您需要两个groupingBy收集器。第一组将按小时分组:

代码语言:javascript
复制
Collector<TimeEntry, ?, Map<Integer, List<TimeEntry>>> groupingByHours
    = Collectors.groupingBy(TimeEntry::getHour);

第二组分几天。

代码语言:javascript
复制
Collector<TimeEntry, ?, Map<Integer, Map<Integer, List<TimeEntry>>>> groupingByDaysByHour
    = Collectors.groupingBy(TimeEntry::getDay, groupingByHours);

然后,您可以将TimeEntry列表按天分组,然后按小时分组,如下所示:

代码语言:javascript
复制
Map<Integer, Map<Integer, List<TimeEntry>>> dayMap = 
    timeEntries.stream()
    .collect( groupingByDaysByHour);

这将提供以下方面的产出:

代码语言:javascript
复制
{22={10=[2016-01-22T10:00 PT5M]}, 23={8=[2016-01-23T08:00 PT7M, 2016-01-23T08:43 PT3M]}}

为了完整起见,对原始TimeEntry类进行了修改,以处理此示例:

代码语言:javascript
复制
class TimeEntry {
    private LocalDateTime start;
    private Duration duration;

    public TimeEntry(String start, String duration) {
        this.start = LocalDateTime.parse(start, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
        this.duration = Duration.parse(duration);
    }

    public int getDay() {
        return start.get(ChronoField.DAY_OF_MONTH);
    }
    public int getHour() {
        return start.get(ChronoField.HOUR_OF_DAY);
    }
    public long getDuration() {
        return duration.toMinutes();
    }
    @Override
    public String toString() {
        return start.toString() + " " + duration.toString();
    }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34975660

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档