Master Java Stream API: Real‑World Examples Covering 90% of Business Scenarios
This article introduces Java 8 functional programming concepts such as lambda expressions and method references, explains the Stream API, and provides dozens of practical code snippets for querying, grouping, aggregating, sorting, partitioning, and parallel processing of collections, helping developers handle most everyday business logic efficiently.
Java 8 introduced functional programming features, including lambda expressions and method references, allowing functions to be passed as parameters and simplifying code abstraction (y = f(x), where x is input and y is output).
The core of functional programming in Java is the java.util.stream.Stream interface, which enables fluent operations on collections.
Stream API Examples
The following code snippets summarize more than 90% of typical business scenarios and are recommended for Java beginners.
1. Use in Instead of for Loop Query
List<String> userIds = users.stream()
.map(TestUser::getUserId)
.collect(Collectors.toList());
LambdaQueryWrapper<TestOrder> wrapper = Wrappers.lambdaQuery(TestOrder.class)
.in(TestOrder::getUserId, userIds)
.orderByDesc(TestOrder::getOrderId);
List<Order> orders = orderService.list(wrapper);
Map<String, List<TestOrder>> userOrderMap = orders.stream()
.collect(Collectors.groupingBy(TestOrder::getUserId));2. List to Map Transformations
2.0 Group by Single Field
Map<String, List<TestUser>> userOrderMap = users.stream()
.collect(Collectors.groupingBy(user -> {
if (user.getRemainAmount().compareTo(new BigDecimal(10000)) >= 0) {
return "HighValue";
} else if (user.getRemainAmount().compareTo(new BigDecimal(1000)) >= 0) {
return "MidValue";
} else {
return "LowValue";
}
}));2.1 Group by Multiple Fields (Composite Key)
Map<String, List<TestUser>> userOrderMap = users.stream()
.collect(Collectors.groupingBy(user -> user.getRemainAmount() + "_" + user.getName()));2.2 Aggregations on List Fields
// Sum
Map<Integer, Integer> sumMap = orders.stream()
.collect(Collectors.groupingBy(TestOrder::getStatus, Collectors.summingInt(TestOrder::getNum)));
// Count
Map<Integer, Long> countMap = orders.stream()
.collect(Collectors.groupingBy(TestOrder::getStatus, Collectors.counting()));
// Average
Map<Integer, Double> avgMap = orders.stream()
.collect(Collectors.groupingBy(TestOrder::getStatus, Collectors.averagingInt(TestOrder::getNum)));
// Sum BigDecimal
Map<String, BigDecimal> priceMap = orders.stream()
.collect(Collectors.groupingBy(TestOrder::getProductId,
Collectors.mapping(TestOrder::getRewardAmt, Collectors.reducing(BigDecimal.ZERO, BigDecimal::add))));2.3 Find Min/Max in Groups
// Minimum price per status
Map<Integer, TestOrder> minMap = orders.stream()
.collect(Collectors.groupingBy(TestOrder::getStatus,
Collectors.collectingAndThen(Collectors.minBy(Comparator.comparing(TestOrder::getPrice)),
opt -> opt.orElse(null))));
// Maximum price per status
Map<Integer, TestOrder> maxMap = orders.stream()
.collect(Collectors.groupingBy(TestOrder::getStatus,
Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparing(TestOrder::getPrice)),
opt -> opt.orElse(null))));2.4 Preserve Insertion Order
Map<Integer, List<TestOrder>> orderedMap = orders.stream()
.collect(Collectors.groupingBy(TestOrder::getStatus, LinkedHashMap::new, Collectors.toList()));2.5 Sort List by Field
// Sort by price descending and then group by order name
Map<String, List<TestOrder>> sortedGroup = orders.stream()
.sorted(Comparator.comparing(TestOrder::getPrice).reversed())
.collect(Collectors.groupingBy(TestOrder::getOrderName));2.6 Convert List to Other Objects
List<TestUserDto> dtoList = users.stream()
.map(user -> BeanUtil.copyProperties(user, TestUserDto.class))
.collect(Collectors.toList());2.7 Join String Fields
Map<String, String> joinedMap = orders.stream()
.collect(Collectors.groupingBy(TestOrder::getUserId,
Collectors.collectingAndThen(
Collectors.mapping(TestOrder::getOrderName, Collectors.joining(",")),
name -> "Product Names: " + name)));
String phones = list.stream()
.map(Object::getPhone)
.collect(Collectors.joining(","));2.8 Partition by Condition
Map<Integer, Map<Boolean, List<TestUser>>> partitioned = users.stream()
.collect(Collectors.groupingBy(TestUser::getStatus,
Collectors.partitioningBy(user -> user.getAge() > 20)));
Map<Integer, Map<Boolean, Long>> partitionCount = users.stream()
.collect(Collectors.groupingBy(TestUser::getStatus,
Collectors.partitioningBy(user -> user.getAge() > 20, Collectors.counting())));2.9 Custom Mapping and Filtering
Map<String, List<String>> result = list.stream()
.collect(Collectors.groupingBy(coupon -> String.valueOf(coupon.getId()),
Collectors.mapping(coupon -> {
if (coupon.getActivityId().equals(this.actId)) {
return JSONObject.toJSONString(coupon);
} else {
return coupon.getStockCount().toString();
}
}, Collectors.toList())));3. Sum Over a Field
int totalAge = users.stream()
.mapToInt(TestUser::getAge)
.sum();
BigDecimal totalAmount = users.stream()
.map(TestUser::getRemainAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add);4. Sort and Collect
// Collect sorted amounts
List<BigDecimal> sortedAmounts = users.stream()
.map(TestUser::getRemainAmount)
.sorted()
.collect(Collectors.toList());
// Sort by amount then collect objects
List<TestUser> sortedUsers = users.stream()
.sorted(Comparator.comparing(TestUser::getRemainAmount))
.collect(Collectors.toList());5. Filter, Max, Min
// Filter non‑null phone and sum ages
int filteredSum = users.stream()
.filter(u -> u.getPhone() != null)
.mapToInt(TestUser::getAge)
.sum();
// Max by remaining amount
TestUser maxUser = users.stream()
.max(Comparator.comparing(TestUser::getRemainAmount))
.orElse(null);
// Min by remaining amount
TestUser minUser = users.stream()
.min(Comparator.comparing(TestUser::getRemainAmount))
.orElse(null);6. Peek for Debugging
List<BigDecimal> amounts = users.stream()
.map(TestUser::getRemainAmount)
.peek(System.out::println)
.collect(Collectors.toList());7. Skip and Limit
List<TestUser> afterSkip = users.stream()
.skip(5)
.collect(Collectors.toList());
List<TestUser> firstFive = users.stream()
.limit(5)
.collect(Collectors.toList());8. Flatten Multiple Lists
List<TestOrder> allOrders = userDtos.stream()
.flatMap(dto -> dto.getOrders().stream())
.collect(Collectors.toList());9. Find First Element
TestUserDto firstDto = userDtos.stream()
.findFirst()
.orElse(null);10. Parallel Stream for Large Data
TestUserDto parallelFirst = userDtos.stream()
.parallel()
.findFirst()
.orElse(null);11. MyBatis‑Plus Grouping with Count and Having
LambdaQueryWrapper<TestOrder> wrapper = new QueryWrapper<TestOrder>()
.select("*, count(*) as count")
.lambda()
.groupBy(TestOrder::getUserId)
.orderByDesc(TestOrder::getOrderId)
.having("count(*) > {0}", 10);The article concludes with a reminder to experiment with these patterns, adapt them to your own domain models, and share feedback for continuous improvement.
Lin is Dream
Sharing Java developer knowledge, practical articles, and continuous insights into computer engineering.
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.
