使用Java Stream计算项目列表中日期的出现次数

Kas*_*rer 11 java datetime java-8 java-stream

我有一个带有(java.util.)Date属性的项目列表,我想为从最早的日期到现在的每一天创建一个DataSeriesItem .这是一个带有时间轴的图表系列.

该DataSeriesItem的创建将如下所示:
DataSeriesItem seriesItem = new DataSeriesItem(Date, occurrenceCount);
其中occurrenceCountDate属性与该日匹配的Items的数量.第一个参数也可以是类型java.time.Instant

我已经设法找到一种有效的方法,但我确信我的方法非常糟糕,并且可以用一个流来完成,也许两个.但是,我是溪流的初学者,不能用我的知识来做.

这有可能与流?它怎么可能会像
我不是让你真正做到我的整个重新执行,但只有我指向正确的streamfunctions和映射,你会使用,而加分它的一个例子.

这是我丑陋的解决方案:

List<?> items = myItems;
Collection<Date> foundDates = new HashSet<>();

for (Object item : items) {
    foundDates.add((Date)getPropertyValueFromItem(item, configurator.getDateProperty()));
}

//======  This is the part I am asking about ======//

Map<Instant, Integer> foundInstants = new HashMap<>();
foundDates.stream().sorted(Date::compareTo).forEach(date -> {
    Calendar c = Calendar.getInstance();
    c.clear(); // clear nanoseconds, or else equals won't work!
    c.set(date.getYear()+1900, date.getMonth(), date.getDate(), 0, 0, 0);
    if(!foundInstants.containsKey(c.toInstant())){
        foundInstants.put(c.toInstant(), 1);
    } else {
        // increment count of that entry
        Integer value = foundInstants.get(c.toInstant());
        foundInstants.remove(c.toInstant());
        foundInstants.put(c.toInstant(), ++value);
    }
});

//====== Leaving this code here for context ======//  
// Could this maybe simplyfied too by using streams  ?

// find oldest date
Date dateIndex = foundDates.stream().min(Date::compareTo).get();
Date now = new Date();

// starting from oldest date, add a seriesItem for each day until now
// if dateOccurrences contains the current/iterated date, use it's value, else 0
while(dateIndex.before(now)){
    Calendar c = Calendar.getInstance();
    c.clear();// clear nanoseconds, or else equals won't work!
    c.set(dateIndex.getYear()+1900, dateIndex.getMonth(), dateIndex.getDate(), 0, 0, 0);

    if(foundInstants.containsKey(c.toInstant())){
        ExtendedDataSeriesItem seriesItem = new ExtendedDataSeriesItem(c.toInstant(), foundInstants.get(c.toInstant()));
        seriesItem.setSeriesType("singleDataPoint");
        series.add(seriesItem);
    } else {
        ExtendedDataSeriesItem seriesItem = new ExtendedDataSeriesItem(c.toInstant(), 0);
        seriesItem.setSeriesType("singleDataPoint");
        series.add(seriesItem);
    }
    c.add(Calendar.DATE, 1); // adding a day is complicated. Calendar gets it right. Date does not. This is why I don't use Date here
    dateIndex = c.getTime();
}
Run Code Online (Sandbox Code Playgroud)

dan*_*niu 8

您可以使用groupingBy()然后使用下游收集器counting().

Map<Date, Long> occurrances = dateList.stream().collect(
                  groupingBy(d -> yourTransformation(d), counting()));
Run Code Online (Sandbox Code Playgroud)

DataSeriesItem从该地图创建对象应该很容易.


Nam*_*man 5

算上你正在寻找类似的东西:

Map<Instant, Long> foundInstants =  foundDates.stream()
            .collect(Collectors.groupingBy(Date::toInstant, Collectors.counting()));
Run Code Online (Sandbox Code Playgroud)

除此之外,您可以将这些内容缩短if..else为:

ExtendedDataSeriesItem seriesItem = 
        new ExtendedDataSeriesItem(c.toInstant(), foundInstants.getOrDefault(c.toInstant(), 0L));
seriesItem.setSeriesType("singleDataPoint");
series.add(seriesItem);
Run Code Online (Sandbox Code Playgroud)

这就是说你应该同时寻找迁移LocalDateTime并避免使用Date.