Advanced Java Stream APIs: Reduce, Match, FlatMap and Performance Tips
The article explores lesser‑used Java Stream APIs—reduce, the matching trio (allMatch, anyMatch, noneMatch) and flatMap—showing practical examples, performance impacts of operation ordering, readability trade‑offs, and when to prefer streams or traditional loops, even mentioning parallelStream considerations.
Java Stream API offers elegant functional‑style operations, but several useful methods are less frequently used. This article introduces uncommon APIs such as reduce , the matching family ( allMatch , anyMatch , noneMatch ) and flatMap , and discusses their performance implications.
reduce takes three parameters – an identity value, an accumulator and a combiner. The article shows a concrete case with a @Data @AllArgsConstructor public static class Calendar { private LocalDate date; private boolean today; private boolean signed; } and demonstrates how to compute results using reduce while printing IDE execution results.
@Data
@AllArgsConstructor
public static class Calendar {
private LocalDate date;
private boolean today;
private boolean signed;
}
LocalDate now = new LocalDate();
List
calendars = Arrays.asList(
new Calendar(new LocalDate(1661174238000L), false, false),
new Calendar(new LocalDate(1661828371000L), false, false),
new Calendar(new LocalDate(1661433438000L), false, false),
new Calendar(new LocalDate(1661519838000L), false, false),
new Calendar(new LocalDate(1661779038000L), false, false),
new Calendar(now, true, true)
);
// anyMatch version
boolean yesterdaySigned = calendars.stream()
.anyMatch(t -> Days.daysBetween(t.getDate(), now).getDays() == 1 && t.isSigned());
System.out.println("昨天是否签到过 -> " + yesterdaySigned);
// loop version
boolean yesterdaySigned2 = false;
for (Calendar calendar : calendars) {
if (Days.daysBetween(calendar.getDate(), now).getDays() == 1) {
yesterdaySigned2 = calendar.isSigned();
break;
}
}
System.out.println("昨天是否签到过写法二 -> " + yesterdaySigned2);The matching methods ( allMatch , anyMatch , noneMatch ) are illustrated with a simple check whether all, any or none of the elements satisfy a predicate. Example code:
boolean yesterdaySigned = calendars.stream()
.filter(t -> Days.daysBetween(t.getDate(), now).getDays() == 1)
.anyMatch(Calendar::isSigned);flatMap differs from map by allowing one element to be transformed into multiple elements, which is useful for flattening nested collections. The article includes an IDE screenshot and explains why flatMap can be more efficient in certain scenarios.
Performance considerations are highlighted, especially the impact of operation order. Placing filter before other operations can greatly reduce the amount of data processed, as shown in the comparison between map‑filter‑foreach and filter‑map‑foreach pipelines. A concrete example:
List
awardId = timeFilterAwardConfigs.stream()
.map(config -> config.getAwardId())
.filter(id -> id > 0)
.collect(Collectors.toList());Another example demonstrates two ways to calculate total company turnover for users, weighing filter‑first versus map‑first strategies based on filter selectivity and the cost of the auxiliary queryUserCompany call.
int allCompanyTurnover1 = userList.stream()
.map(user -> calculateAnnualTurnover(queryUserCompany(user)))
.filter(Objects::nonNull)
.reduce(0, Integer::sum);
int allCompanyTurnover2 = userList.stream()
.filter(user -> {
Company company = queryUserCompany(user);
return company != null && !"余杭".equals(company.getLocal());
})
.map(user -> calculateAnnualTurnover(queryUserCompany(user)))
.reduce(0, Integer::sum);Readability is also discussed. While stream code can be concise, it may become harder to read in complex scenarios. The article compares a traditional loop with a stream‑based implementation for counting sign‑in actions within a date range:
private int getCycleActionCount(Date start, Date end, List
calendar) {
int count = 0;
for (ActionCalendar day : calendar) {
Date d = day.getDate();
if (d.after(start) && d.before(end) && day.isComplete()) {
count++;
}
}
return count;
}
private int getCycleActionCount2(Date start, Date end, List
calendar) {
return Math.toIntExact(
calendar.stream()
.filter(t -> t.getDate().after(start) && t.getDate().before(end) && t.isComplete())
.count()
);
}In summary, streams are powerful for many cases, but developers should carefully consider operation ordering, lambda complexity, and whether a stream is appropriate for a given problem to avoid performance or readability drawbacks. The article also mentions that parallelStream exists for very large collections but brings additional considerations.
DaTaobao Tech
Official account of DaTaobao Technology
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.