Stream 实战:博客列表排序、过滤与分页(AI 辅助学习 Java 8)

1 阅读3分钟

前两周学了 Lambda、函数式接口和 Stream 基础。今天在一个完整的博客接口场景中,用 Stream 实现排序、过滤、分页、统计功能。


一、通用分页:排序 + skip + limit

分页是后端最常见的功能。用 Stream 实现只需要三行:

public List<Blog> getBlogsByPage(int page, int pageSize) {
    return blogs.stream()
            .sorted(Comparator.comparing(Blog::getCreateTime).reversed()) // 最新在前
            .skip((long) (page - 1) * pageSize)   // 跳过前面的数据
            .limit(pageSize)                       // 取一页
            .collect(Collectors.toList());
}

核心公式skip((page - 1) * pageSize)

页码pageSizeskiplimit
第1页10010
第2页101010
第3页102010

二、带条件的分页:先过滤再分页

实际场景中,分页往往带有筛选条件:

// 只看已发布的文章,按阅读量降序分页
public List<Blog> getPublishedByPage(int page, int pageSize) {
    return blogs.stream()
            .filter(Blog::isPublished)
            .sorted(Comparator.comparingInt(Blog::getViews).reversed())
            .skip((long) (page - 1) * pageSize)
            .limit(pageSize)
            .collect(Collectors.toList());
}

三、搜索 + 分页

关键词搜索后分页返回:

public List<Blog> searchAndPaginate(String keyword, int page, int pageSize) {
    return blogs.stream()
            .filter(b -> b.getTitle().toLowerCase().contains(keyword.toLowerCase())
                    || b.getAuthor().contains(keyword))
            .skip((long) (page - 1) * pageSize)
            .limit(pageSize)
            .collect(Collectors.toList());
}

// 同时获取总数
long total = blogs.stream()
        .filter(b -> b.getTitle().toLowerCase().contains(keyword.toLowerCase()))
        .count();

四、统计面板:分组 + 聚合

按作者统计文章数和总阅读量

Map<String, AuthorStats> stats = blogs.stream()
        .collect(Collectors.groupingBy(
                Blog::getAuthor,
                Collectors.collectingAndThen(
                        Collectors.toList(),
                        list -> new AuthorStats(
                                list.size(),
                                list.stream().mapToInt(Blog::getViews).sum()
                        )
                )
        ));

输出:

张三: 3 篇, 总阅读 530
李四: 2 篇, 总阅读 750
王五: 2 篇, 总阅读 750

按标签统计文章数

blogs.stream()
        .flatMap(b -> b.getTags().stream())  // 展平所有标签
        .collect(Collectors.groupingBy(tag -> tag, Collectors.counting()))
        .entrySet().stream()
        .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
        .forEach(entry -> System.out.println("  " + entry.getKey() + ": " + entry.getValue() + " 篇"));

输出:

Java: 4 篇
前端: 2 篇
Optional: 1 篇
Docker: 1 篇
...

五、数据校验:allMatch / anyMatch

用 Stream 做数据校验也很方便:

// 所有文章都有标题吗?
boolean allHaveTitle = blogs.stream()
        .allMatch(b -> b.getTitle() != null && !b.getTitle().isEmpty());

// 有没有作者名为空的?
boolean hasEmptyAuthor = blogs.stream()
        .anyMatch(b -> b.getAuthor() == null || b.getAuthor().isEmpty());

六、运行结果

本项目 StreamPaginationDemo.java 完整运行结果:

=== 1. 分页查询 ===
  第 1 页 (每页3条):
    [2026-06-12T12:00] Java 8 Optional 最佳实践
    [2026-06-11T08:00] Docker 入门指南
    [2026-06-09T11:00] React Hooks 教程
  第 2 页:
    [2026-06-07T16:00] Java Stream API 详解
    [2026-06-05T09:00] Vue3 组合式 API
    [2026-06-03T14:00] Spring Boot 实战

=== 2. 带条件分页 ===
  已发布-第1页-按阅读量降序:
    [500阅读] Vue3 组合式 API
    [400阅读] React Hooks 教程

=== 3. 搜索 + 分页 ===
  搜索 'Java'1页:
    Java 8 Lambda 入门 (张三)
    Java Stream API 详解 (张三)
    Java 8 Optional 最佳实践 (张三)
  共 3 条结果

=== 4. 统计面板 ===
  张三: 3 篇, 总阅读 530
  李四: 2 篇, 总阅读 750
  王五: 2 篇, 总阅读 750
  各标签文章数:
    Java: 4 篇
    前端: 2 篇

=== 5. 数据校验 ===
  所有文章都有标题: true
  有空作者名: false

七、本周学习总结

第三周我们学习了 Stream API,完整路线:

周二:Stream API 入门——创建、中间操作、终端操作
周四:filter/map/sorted——三个最常用的操作
周六:分页+搜索+统计——博客接口实战(本篇)

核心收获

知识点关键方法一句话
创建 Streamstream() / of() / generate()数据源 → 流
过滤filter(Predicate)只要匹配的
转换map(Function)一对一转换
扁平化flatMap一对多再展平
排序sorted(Comparator)按规则排序
分页skip(n) + limit(n)跳过 + 取
统计count() / sum() / average()聚合计算
匹配anyMatch / allMatch / noneMatch是否有匹配

八、下周预告

第四周进入 Stream 进阶:Collectors 全家桶(toList/joining/groupingBy/partitioningBy)、flatMap 深入、以及数据统计的完整实战。


系列文章导航

更多请关注我的微信公众号,感谢!

微信图片_20260427132414_18_737.jpg