使用Stream API迭代LocalDate

huangapple go评论85阅读模式
英文:

Iterate through LocalDate wth Stream API

问题

我有三个LocalDate(最近3天),对于每个日期,我想循环遍历并执行一些操作:

StringBuilder result = new StringBuilder();
IntStream.range(0, 3)
    .mapToObj(i -> LocalDate.now().minusDays(i))
    .forEach(currentDate -> {
        Page<User> users = userService.getAllUsersByRegistrationDate(currentDate);
        String reportTable = reportService.getReportTable(users, tableTemplate);
        result.append(reportTable);
    });

如何使用Stream API替换此代码或提高可读性?

英文:

I have three LocalDates (3 last days), and for each date I want to loop through and do some actions:

StringBuilder result = &quot;&quot;;
for (LocalDate currentDate = LocalDate.now(); currentDate.isAfter(LocalDate.now().minusDays(3));  currentDate = currentDate.minusDays(1)){
     Page&lt;User&gt; users = userService.getAllUsersByRegistrationDate(currentDate);
     String reportTable = reportService.getReportTable(users, tableTemplate);
     result.append(reportTable);
}

How can I replace this code with Stream API or improve readability?

答案1

得分: 4

LocalDate.datesUntil(LocalDate, Period)

我认为你的代码目前看起来很好。如果你想使用流操作,或者你只是好奇它可能是什么样子,自从Java 9以来,你可以这样做:

LocalDate today = LocalDate.now(ZoneId.of("Africa/Timbuktu"));
today.datesUntil(today.minusDays(3), Period.ofDays(-1))
    .forEach(ld -> {
        System.out.println("现在正在处理日期 " + ld);
    });

在运行时的输出:

现在正在处理日期 2020-10-15
现在正在处理日期 2020-10-14
现在正在处理日期 2020-10-13

并没有很好地记录LocalDate.datesUntil()可以使用负周期,但它确实返回一个LocalDate对象的流。

(我知道在这种情况下lambda中的花括号是不必要的,但从你的问题中看出,你可能想要为每个日期执行多个语句,因此它们是必需的。当然,如果你只想要一个方法调用,你可能只需要一行。.forEach(ld -> System.out.println("现在正在处理日期 " + ld))。)

文档链接:LocalDate.datesUntil()

英文:

LocalDate.datesUntil(LocalDate, Period)

I find your code fine as i stands. If you want to use a stream operation, or you’re just curious how one might look, since Java 9 you may do:

	LocalDate today = LocalDate.now(ZoneId.of(&quot;Africa/Timbuktu&quot;));
	today.datesUntil(today.minusDays(3), Period.ofDays(-1))
			.forEach(ld -&gt; {
				System.out.println(&quot;Now doing something with date &quot; + ld);
			});

Output when running just now:

> Now doing something with date 2020-10-15
> Now doing something with date 2020-10-14
> Now doing something with date 2020-10-13

It’s not that well documented that LocalDate.datesUntil() works with a negative period, but it does and returns a stream of LocalDate objects.

(I know the curly braces in the lambda are unnecessary in this case, but it seems from your question that you wanted to do more than one statement for each date, and so they are necessary, of course. If you only wanted one method call there, you would probably want just one line. .forEach(ld -&gt; System.out.println(&quot;Now doing something with date &quot; + ld)).)

Documentation link: LocalDate.datesUntil()

答案2

得分: 3

<h3>Java 8解决方案</h3>

如果您坚持使用[tag:java-8],您唯一能做的就是创建一个`LocalDate`序列,使用一个由您想要过去多少天的`IntStream`来表示。然后使用`mapToObj`创建相关的`LocalDate`。

```java
LocalDate currentDate = LocalDate.now();
String result = IntStream.range(0, 3)
    .mapToObj(currentDate::minusDays)
    .map(date -> {
        Page<User> users = userService.getAllUsersByRegistrationDate(date);
        return reportService.getReportTable(users, tableTemplate);
    })
    .collect(Collectors.joining());

Java 9+解决方案

如果您使用[tag:java-9]或更高版本,您可以利用无限流(Stream)并保持生成项目,直到满足某个条件为止。

三参数 Stream.iterate​(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)

LocalDate threeDaysAgo = currentDate.minusDays(3);
String result = Stream.iterate(
        LocalDate.now(), 
        date -> date.isAfter(threeDaysAgo), 
        date -> date.minusDays(1))
    .map(date -> {
        Page<User> users = userService.getAllUsersByRegistrationDate(date);
        return reportService.getReportTable(users, tableTemplate);
    })
    .collect(Collectors.joining());

一个新方法 Stream::takeWhile(Predicate<? super T> predicate)

LocalDate threeDaysAgo = currentDate.minusDays(3);
String result = Stream.iterate(LocalDate.now(), date -> date.minusDays(1))
    .takeWhile(date -> date.isAfter(threeDaysAgo))
    .map(date -> {
         Page<User> users = userService.getAllUsersByRegistrationDate(date);
         return reportService.getReportTable(users, tableTemplate);
    })
    .collect(Collectors.joining());

上面的所有示例都生成了3个LocalDate实例,因为今天是2020-10-15,它们会生成过去3天的日期:2020-10-152020-10-142020-10-13,并通过Stream<LocalDate>供进一步处理。

免责声明:我认为使用for循环更易读。不要期望将“转换”为Stream API的for循环会自动增加可读性和简洁性。对于许多情况,事实并非如此。


<details>
<summary>英文:</summary>

&lt;h3&gt;Java 8 solution&lt;/h3&gt;

If you are really stick with [tag:java-8], all you can do is to create a sequence of `LocalDate` using an `IntStream` of that many days you want to go to past with. Then `mapToObj` to create a relevant `LocalDate`.

LocalDate currentDate = LocalDate.now();
String result = IntStream.range(0, 3)
.mapToObj(currentDate::minusDays)
.map(date -> {
Page<User> users = userService.getAllUsersByRegistrationDate(date);
return reportService.getReportTable(users, tableTemplate);
})
.collect(Collectors.joining());

__________

&lt;h3&gt;Java 9+ solutions&lt;/h3&gt;

If you use [tag:java-9] or later, you can the advantage of infinite Stream and keep generating items until a certain condition is met.

**Three-args** [`Stream.iterate​(T seed, Predicate&lt;? super T&gt; hasNext, UnaryOperator&lt;T&gt; next)`][1]

LocalDate threeDaysAgo = currentDate.minusDays(3);
String result = Stream.iterate(
LocalDate.now(),
date -> date.isAfter(threeDaysAgo),
date -> date.minusDays(1))
.map(date -> {
Page<User> users = userService.getAllUsersByRegistrationDate(date);
return reportService.getReportTable(users, tableTemplate);
})
.collect(Collectors.joining());


**A new method** [`Stream::takeWhile(Predicate&lt;? super T&gt; predicate)`][2]

LocalDate threeDaysAgo = currentDate.minusDays(3);
String result = Stream.iterate(LocalDate.now(), date -> date.minusDays(1))
.takeWhile(date -> date.isAfter(threeDaysAgo))
.map(date -> {
Page<User> users = userService.getAllUsersByRegistrationDate(date);
return reportService.getReportTable(users, tableTemplate);
})
.collect(Collectors.joining());

_____

All the examples above generate 3 `LocalDate` instances, since today is `2020-10-15`, they will have for past 3 days: `2020-10-15`, `2020-10-14`, `2020-10-13` and offer them for further processing through `Stream&lt;LocalDate&gt;`.

**Disclaimer**: I find the for-loop more readable. Don&#39;t expect &quot;converted&quot; for-loop into Stream API adds readability and brevity automatically. For a lot of cases, it doesn&#39;t.


  [1]: https://docs.oracle.com/javase/9/docs/api/java/util/stream/Stream.html#iterate-T-java.util.function.Predicate-java.util.function.UnaryOperator-
  [2]: https://docs.oracle.com/javase/9/docs/api/java/util/stream/Stream.html#takeWhile-java.util.function.Predicate-

</details>



huangapple
  • 本文由 发表于 2020年10月15日 20:52:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/64372028.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定