在Java 8中收集流的元素,并将其中的一些元素减少为生成的集合中的单个元素

时间:2014-10-02 18:35:13

标签: java lambda java-8 java-stream

以下流操作会生成Stream<DateCount>,其中每个元素表示具有关联计数的日期序列。如果我有星期一到星期日的元素,并且我想将星期五,星期六或星期日汇总成一个新的DateCount元素,我将如何实现这一目标?最终,我得到的流应该只包含星期一到星期五的元素,但星期五的计数应该是星期五,星期六和星期日的计数总和。我想我需要为此创建Collector,但我不确定如何让收藏家返回我正在寻找的内容。

IntStream.range(0, days)
        .mapToObj(idx -> new DateTime().withTimeAtStartOfDay().minusDays(idx).toDate())
        .map(date -> {
            Tuple<Date, Long> dateLongTuple = data.stream()
                    .map(arr -> new Tuple<>(DateTime.parse(arr[0].toString(), DATE_FORMATTER).toDate(), (Long) arr[1]))
                    .filter(tuple -> date.equals(tuple.getX()))
                    .findFirst().orElse(new Tuple<>(date, 0L));

            return new DateCount(dateLongTuple.getX(), dateLongTuple.getY());
        })

2 个答案:

答案 0 :(得分:1)

List<IntPair<DayOfWeek>> result = Arrays.stream(DayOfWeek.values())
        .map(day -> new IntPair<>(day, 1))
        .collect(ArrayList::new,
                (list, pair) -> {
                    switch (pair.x) {
                        case SATURDAY:
                        case SUNDAY:
                            list.get(list.size() - 1).add(pair.y);
                            break;
                        default:
                            list.add(pair);
                    }
                },
                ArrayList::addAll);
System.out.println(result);

我的元组实现:

class IntPair<T> {
    T x;
    int y;

    IntPair(T x, int y) {
        this.x = x;
        this.y = y;
    }

    void add(int i) {
        y += i;
    }

    @Override
    public String toString() {
        return "(" + x + ", " + y + ")";
    }
}

答案 1 :(得分:1)

您的问题有点不明确:

  • 流中的日期是唯一的吗?
  • 流是否仅涵盖一周,或者如果不是,则每天的日期汇总或者仍然是日历的不同日期,但只汇总三个相邻日期?
  • 如何汇总?您指的是我们不知道的课程DateCount,但在您的代码示例中使用了Tuple<Date, Long>
  • 您似乎没有使用Java 8日期/时间API。您使用的是哪种API,为什么不使用Java 8 API?

也许你有一个“XY Problem”

我将给你一个使用Java 8 API的例子,它可能更接近你的实际任务(利用your other question的信息):

  • BigIntegerLong个对象的二维数组中创建一个流,我假设它代表日历日和计数
  • 过滤它们以包含特定范围的日期,即从今天开始的一周
  • 将它们按周划分,由于范围有限,现在是唯一的,总结long
  • 除了您之前的问题,我们会将SATURDAYSUNDAY视为FRIDAY,形成一个群组

...

long min = LocalDate.now().getLong(ChronoField.EPOCH_DAY),
     max=min+DayOfWeek.values().length;

Map<DayOfWeek, Long> map = data.stream()
  .map(arr -> new Tuple<TemporalAccessor,Long>(
       DateTimeFormatter.BASIC_ISO_DATE.parse(arr[0].toString()), (Long)arr[1]))
  .filter(t->{ long d=t.getX().getLong(ChronoField.EPOCH_DAY); return d>=min&&d<max;})
  .collect(groupingBy(t -> weekdayOrFriday(t.getX()), summingLong(t -> t.getY())));
System.out.println(map);

static DayOfWeek weekdayOrFriday(TemporalAccessor d) {
    final DayOfWeek day = DayOfWeek.from(d);
    switch(day) {
        default: return day;
        case SATURDAY: case SUNDAY: return DayOfWeek.FRIDAY;
    }
}

这是一个替代解决方案,它将分组到日历日(在周末天数折叠到前一个星期五之后),如果范围可能大于一周并且您实际上并不想汇总相同的工作日,这将是正确的解决方案

LocalDate min=LocalDate.now(), max=min.plusDays(DayOfWeek.values().length);
data.stream()
    .map(arr -> new Tuple<TemporalAccessor,Long>(
            DateTimeFormatter.BASIC_ISO_DATE.parse(arr[0].toString()), (Long)arr[1]))
    .map(t->new Tuple<LocalDate,Long>(fridayarize(t.getX()), t.getY()))
    .filter(t -> !t.getX().isBefore(min) && t.getX().isBefore(max) )
    .collect(groupingBy(Tuple::getX, TreeMap::new, summingLong(Tuple::getY)))
    .forEach((date,count)->System.out.println(date+"\t: "+count));

static LocalDate fridayarize(TemporalAccessor d) {
    LocalDate date = LocalDate.from(d);
    switch(DayOfWeek.from(d)) {
        default: return date;
        case SATURDAY: return date.minusDays(1);
        case SUNDAY: return date.minusDays(2);
    }
}

但是,在您对实际问题做出更准确的描述之前,我将不再发布更多解决方案......