如何流式传输对象地图并将地图展平为新对象的单个列表?

时间:2016-12-16 14:49:03

标签: lambda java-8 java-stream

我正在尝试找出处理以下场景的最佳方法。

我有一个按时间戳分组的数据库中发生的事件地图:

[2016-04-01 00:00:00, [event1, event2, event 3]]
[2016-02-01 00:00:00, [event5, event6]]
[2016-01-01 00:00:00, [event7, event8, event9, event10]]

我想在tableview中显示这些数据,每个事件时间戳只有一行。但是,事件中的某些值可能会重复。

例如:

事件定义是

class Event () {    
    LocalDateTime timestamp
    String eventName
    String eventDescription
}

我当时想要扁平化到这样的结构

class DisplayEvent () {    
    LocalDateTime timestamp
    Collection<String> eventName
    Collection<String> eventDescription
}

因此,在转换原始地图后,我留下以下列表:

displayEvent1
displayEvent2
displayEvent3

其中每个显示事件都包含原始事件的键,但是包含所有可变数据的数组。

我按如下方式创建了地图:

Map<LocalDateTime, List<Event>> eventsByTimestamp =
                    events
                    .stream()
                    .collect(Collectors.groupingBy(ev -> ev.timestampProperty().get()));

希望这是有道理的。

2 个答案:

答案 0 :(得分:1)

在我看来,最好的方法取决于你的任务细节。

更通用的方法是类Event

public class Event
{
    LocalDateTime timestamp;
    String eventName;
    String eventDescription;
}

然后,您可以按时间戳优雅地创建分组:

event.
    stream().
    collect(groupingBy(Event::getTimeStamp));

这当然不是那么有效,并且适用于较少的事件。因此,您可以考虑在从数据库中提取事件时对事件进行分组,例如:

public class EventRepository
{
    Map<LocalDateTime, List<Event>> getAllEventsGroupedByTime(); 
}

getAllEventsGroupedByTime中,您将实例化事件对象并将它们放在相应的组中,而不在另一个应用程序层中重复此计算。

替代方法是这样的:

public class Event
{
    String eventName;
    String eventDescription;
}

public class EventGroup
{
     LocalDateTime timestamp;
     List<Event> events;
}

public class EventGroupRepository
{
    List<EventGroups> getEventGroups(); 
}

老实说,我发现第二个解决方案没有什么好处(例如,一些对象引用不受影响)。所以我会选择no.1并在从DB中提取它们时对它们进行分组,特别是如果事件的数量不是那么大的话。这似乎更性感,因为事件时间自然属于事件。

<强>更新

好吧,您似乎希望使用很酷的java 8流将所有事件转换为显示事件的集合:-)然后,您可以考虑将以下简单方法添加到DisplayEvent(或创建某种类型的助手班):

public static DisplayEvent combine(DisplayEvent evt1, DisplayEvent evt2)
{
    if (evt1.getActivityTimestamp() == null)
        evt1.setActivityTimestamp(evt2.getActivityTimestamp());

    //TODO: transfer all names from evt2 to evt 1
    // check if timestamps match, optimize, etc

    return evt1;
}

public static DisplayEvent fromEvent(Event evt)
{
    DisplayEvent dispEvt = new DisplayEvent();

    //todo copy the necessary stuff to dispEvt

    return dispEvt;
}

然后lambda可能看起来像这样:

Collection<DisplayEvent> displayEvents = 
              events.stream().collect(Collectors.groupingBy(
                        ev -> ev.activityTimestampProperty(),
                        Collectors.reducing(
                                        new DisplayEvent(), 
                                        DisplayEvent::fromEvent, 
                                        DisplayEvent::combine))).values();

免责声明:我不太喜欢这个:-)所以我个人坚持使用非DisplayEvent解决方案。

答案 1 :(得分:0)

这是我的解决方案,但不完全是我希望的lambda表达式。

events
    .stream()
    .collect(Collectors.groupingBy(ev -> ev.activityTimestampProperty().get()))
    .forEach((key,v) -> {
        DisplayEvent de = new DisplayEvent();
        de.activityTimestamp.set(key);
        for (Event event : v) {
            de.userName.set(event.getUserName());
            de.eventDescriptions.add(event.getEventDescription());
            de.eventNames.add(event.geEventName());
        }
        displayActivities.add(dha);
    });