Java stream filter sum of values

|▌冷眼眸甩不掉的悲伤 提交于 2019-12-06 23:30:48

问题


I have a class called MonitoredData with which describes an activity, its starting time and ending time. The attributes are activityLabel, startTime, endTime. I have to group and filter these activities, using streams, the activities which have the total duration of more than 10 hours. I managed to make the sum of durations and group them according to the activity using this:

Map<String, Long> map4 = new HashMap<String, Long>();

map4 = data.stream()
    .collect(
        Collectors.groupingBy(
            MonitoredData::getActivity, 
            Collectors.summingLong(MonitoredData::getDuration)
        )
    ); //getDuration returns end Time - startTime in milliseconds

But I haven't managed to add a filter. I tried using:

.filter(Collectors.summingLong(MonitoredData::getDuration) > whatever)

but obviously it doesn't work. How can I solve this in order to make it return a Map<String, Long>?


回答1:


I would first do as you've already done: I'd collect the stream of MonitoredData instances to a map, grouping by activity and summing the duration of each activity in each value:

Map<String, Long> map4 = data.stream()
    .collect(Collectors.groupingBy(
        MonitoredData::getActivity, 
        HashMap::new,
        Collectors.summingLong(MonitoredData::getDuration)));

The nuance is that I'm using the overloaded version of Collectors.groupingBy that accepts a factory for the map, because in the next step I want to remove the entries whose duration is less than 10 hours, and the spec doesn't guarantee that the map returned by the Collectors.groupingBy methods that take either one or two arguments is mutable.

This is how I'd remove the entries that don't match:

public static final long TEN_HOURS_MS = 10 * 60 * 60 * 1000;

map4.values().removeIf(v -> v < TEN_HOURS_MS);

If you want to do everything in a single line, you might want to use Collectors.collectingAndThen:

Map<String, Long> map4 = data.stream()
    .collect(Collectors.collectingAndThen(
        Collectors.groupingBy(
            MonitoredData::getActivity, 
            HashMap::new,
            Collectors.summingLong(MonitoredData::getDuration)),
        m -> { m.values().removeIf(v -> v < TEN_HOURS_MS); return m; } ));

Here I've used Collectors.collectingAndThen to modify the map returned by Collectors.groupingBy. And, within the finisher function, I've used Collection.removeIf, which takes a predicate and removes all the entries that match that predicate.




回答2:


How about this? I answer on phone, you need test by yourself.

map = map.entrySet()
         .stream()
         .filter(it->TimeUnit.MILLISECONDS.toHours(it.getValue())>10)
         .collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue));

OR using Collectors#collectingAndThen:

map4 = data.stream()
.collect(Collectors.collectingAndThen(    
    Collectors.groupingBy(
        MonitoredData::getActivity, 
        Collectors.summingLong(MonitoredData::getDuration)
    ),
    r1 -> r1.entrySet().stream()
            .filter(it->TimeUnit.MILLISECONDS.toHours(it.getValue())>10)
            .collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue))
));



回答3:


I think this is what you wanted, using Google Guava Maps class:

Map<String, Long> map = Maps.filterValues(data.stream()
            .collect(Collectors.groupingBy
                    (MonitoredData::getActivity, Collectors.summingLong
                            (MonitoredData::getDuration)
                    )), value -> value > 3);

You can obviously write your own method to filter map like that, but since it's already there, on so popular library... How to do it with pure streams: I don't know, but maybe this will be satisfying.




回答4:


Add the below code after you got the map:

map4.entrySet().stream()
                .filter(a -> a.getValue() > whatever)
                .collect(Collectors.joining());


来源:https://stackoverflow.com/questions/44101297/java-stream-filter-sum-of-values

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!