用java8流重构

yqlxgs2m  于 2021-07-03  发布在  Java
关注(0)|答案(3)|浏览(467)

我实现了以下类 DateBucket 有一个名为 bucketize . 此方法返回 DateBucket 每个 DateBucket 在列表中的日期之间 fromDate 以及 toDate 和用途 bucketSize 以及 bucketSizeUnit 作为设置 from 以及
to DateBucket 变量。

class DateBucket {
    final Instant from;
    final Instant to;

    public static List<DateBucket> bucketize(
        ZonedDateTime fromDate, 
        ZonedDateTime toDate, 
        int bucketSize, 
        ChronoUnit bucketSizeUnit
        ) {
    List<DateBucket> buckets = new ArrayList<>();

    boolean reachedDate = false;

    for (int i = 0; !reachedDate; i++) {
        ZonedDateTime minDate = fromDate.plus(i * bucketSize, bucketSizeUnit);
        ZonedDateTime maxDate = fromDate.plus((i + 1) * bucketSize, bucketSizeUnit);
        reachedDate = toDate.isBefore(maxDate);
        buckets.add(new DateBucket(minDate.toInstant(), maxDate.toInstant()));
    }
    return buckets;
}
}

例如,以下输入:

fromDate -> 2020-12-07 00:00:00
toDate -> 2020-12-07 00:00:03
bucketSize -> 1
bucketSizeUnit -> ChronoUnit.SECONDS

返回以下内容 DateBucket 列表:

0. DateBucket(from = 2020-12-07  05:00:00, to = 2020-12-07 05:00:01)
1. DateBucket(from = 2020-12-07  05:00:01, to = 2020-12-07 05:00:02)
2. DateBucket(from = 2020-12-07  05:00:02, to = 2020-12-07 05:00:03)
3. DateBucket(from = 2020-12-07  05:00:03, to = 2020-12-07 05:00:04)

那么,如何重构 bucketize 方法使用相同的逻辑,但与Java8流?

yrefmtwq

yrefmtwq1#

这里的思路是,您希望流化创建bucket所需的值,然后 map 以及 collect 他们。溪流 ZonedDateTimes 你可以用 Spliterator 创建流。这似乎是一个可能的实现,它是基本的,不支持并行化,但似乎是可行的。

public class ZonedDateTimeSpliterator implements Spliterator<ZonedDateTime> {
    private final ZonedDateTime fromDate;
    private final ZonedDateTime toDate;
    private final int bucketSize;
    private final ChronoUnit bucketSizeUnit;
    private ZonedDateTime currentDate;

    public ZonedDateTimeSpliterator(ZonedDateTime fromDate, ZonedDateTime toDate, int bucketSize,
            ChronoUnit bucketSizeUnit) {
        this.fromDate = fromDate;
        this.toDate = toDate;
        this.bucketSize = bucketSize;
        this.bucketSizeUnit = bucketSizeUnit;
        currentDate = fromDate.truncatedTo(bucketSizeUnit);
    }

    public void forEachRemaining(Consumer<? super ZonedDateTime> action) {
        while (currentDate.plus(bucketSize, bucketSizeUnit).isBefore(toDate)) {
            getNext(action);
        }
    }

    private void getNext(Consumer<? super ZonedDateTime> action) {
        action.accept(currentDate);
        currentDate = currentDate.plus(bucketSize, bucketSizeUnit);
    }

    @Override
    public boolean tryAdvance(Consumer<? super ZonedDateTime> action) {
        if (currentDate.plus(bucketSize, bucketSizeUnit).isBefore(toDate)) {
            getNext(action);
            return true;
        } else // cannot advance
            return false;
    }

    @Override
    public Spliterator<ZonedDateTime> trySplit() {
        // implement for parallel processing
        return null;
    }

    @Override
    public long estimateSize() {
        return Duration.between(fromDate.truncatedTo(bucketSizeUnit), toDate.truncatedTo(bucketSizeUnit))
                .get(bucketSizeUnit);
    }

    @Override
    public int characteristics() {
        return ORDERED | SIZED | IMMUTABLE;
    }
}

那你就干脆 stream 价值观, map 水桶,还有 collect 名单。

private List<DateBucket> doThing(ZonedDateTime fromDate, ZonedDateTime toDate, int bucketSize,
        ChronoUnit bucketSizeUnit) {
    return StreamSupport
            .stream(() -> new ZonedDateTimeSpliterator(fromDate, toDate, bucketSize, bucketSizeUnit),
                    Spliterator.ORDERED | Spliterator.SIZED | Spliterator.IMMUTABLE, false)
            .map(currentDate -> new DateBucket(currentDate.toInstant(),
                    currentDate.plus(bucketSize, bucketSizeUnit).toInstant()))
            .collect(Collectors.toList());

}
dbf7pr2w

dbf7pr2w2#

您可以通过使用 TemporalUnit#between :

public static List<DateBucket> bucketize(ZonedDateTime fromDate, ZonedDateTime toDate,
    int bucketSize, ChronoUnit bucketSizeUnit) {

    return LongStream.range(0, bucketSizeUnit.between(fromDate, toDate)/bucketSize + 1)
        .mapToObj( nb -> new DateBucket(
            fromDate.plus(nb * bucketSize, bucketSizeUnit).toInstant(),
            fromDate.plus((nb+1) * bucketSize, bucketSizeUnit).toInstant()))
        .collect(Collectors.toList());
}
k7fdbhmy

k7fdbhmy3#

你可以做:

public static List<DateBucket> bucketize(
        ZonedDateTime fromDate, 
        ZonedDateTime toDate, 
        int bucketSize, 
        ChronoUnit bucketSizeUnit
    ) {

    var result = new ArrayList<DateBucket>();
    IntStream.range(0, Integer.MAX_VALUE)
            .mapToObj(i -> fromDate.plus(i * bucketSize, bucketSizeUnit))
            .takeWhile(d -> d.isBefore(toDate))
            .map(d -> d.toInstant())
            .reduce((min, max) -> {
                result.add(new DateBucket(min, max));
                return max;
            });
    return result;
}

虽然这个归约函数可能被认为是对流api的滥用(归约函数应该是无状态和关联的,我们的不是,但是在这种情况下应该是安全的,因为我们知道流是连续的,因此会按顺序迭代),streamapi似乎没有提供更直接的api来组合相邻的流元素。
也就是说,我可能更喜欢你的强制执行。

相关问题