代码之家  ›  专栏  ›  技术社区  ›  kscherrer

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

  •  11
  • kscherrer  · 技术社区  · 7 年前

    我有一个带有(java.util.)Date属性的项目列表,我想创建一个DataSeriesItem 从最早的日期开始到现在。它适用于带有时间线的图表系列。


    DataSeriesItem seriesItem = new DataSeriesItem(Date, occurrenceCount);
    在哪里 occurrenceCount 是其Date属性与当天匹配的项目计数。第一个参数也可以是 java.time.Instant

    我已经设法找到了一种可行的方法,但我确信我的方法是可行的 非常 坏的,可能用一个流完成,也许两个。然而,我是溪流的初学者,用我的知识做不到这一点。

    这可能与流有关吗?可能会是什么样子 大概

    以下是我丑陋的解决方案:

    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();
    }
    
    2 回复  |  直到 7 年前
        1
  •  9
  •   daniu    7 年前

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

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

    创建您的 DataSeriesItem 从该地图中删除对象。

        2
  •  5
  •   Naman    7 年前

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

    除此之外,你还可以缩短这些时间 if..else 进入:

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

    这就是说,您应该同时寻找迁移到 LocalDateTime 避免使用 Date