Java stream groupingBy sorted 实现多条件排序与分组的最佳实践

1. 数据初始化

这一部分代码用于创建 Product 对象并将它们添加到 result 列表中。

java 复制代码
// 初始化数据
List<Product> result = new ArrayList<>();
List<Product> resp = new ArrayList<>();

// 添加产品数据
result.add(new Product("手机A", 1500, "电子", 3, 5));
result.add(new Product("手机B", 1600, "电子", 3, 5));
result.add(new Product("手机C", 1800, "电子", 3, 5));
result.add(new Product("电脑A", 13000, "电子", 3, 6));
result.add(new Product("电脑B", 15000, "电子", 3, 6));
result.add(new Product("电脑C", 18000, "电子", 3, 6));

result.add(new Product("床C", 7000, "家具", 1, 4));
result.add(new Product("椅子A", 500, "家具", 1, 3));
result.add(new Product("椅子B", 400, "家具", 1, 3));
result.add(new Product("椅子C", 300, "家具", 1, 3));

result.add(new Product("牙膏A", 15, "洗护", 2, 1));
result.add(new Product("牙膏B", 25, "洗护", 2, 1));
result.add(new Product("牙膏C", 18, "洗护", 2, 1));
result.add(new Product("洗面奶A", 60, "洗护", 2, 2));
result.add(new Product("洗面奶B", 70, "洗护", 2, 2));
result.add(new Product("洗面奶C", 80, "洗护", 2, 2));
说明
  • Product :表示一个产品对象,包含以下属性:
    • name:产品名称。
    • price:产品价格。
    • type:产品类型(如电子、家具、洗护)。
    • seq0seq1:排序辅助字段。
  • result 列表:存储所有产品对象。
  • resp 列表:用于存储最终输出的结果。

2. 按价格排序并分组

这一部分代码对 result 列表中的产品按价格排序后,按 type 字段分组。

java 复制代码
// 按价格排序并分组
Map<String, List<Product>> collect = result.stream()
        .sorted(Comparator.comparing(Product::getPrice)) // 按价格升序排序
        .collect(Collectors.groupingBy(
                Product::getType, // 按类型分组
                LinkedHashMap::new, // 保持分组顺序
                Collectors.toList() // 分组后每组存为 List
        ));
说明
  • 排序 :使用 Comparator.comparing(Product::getPrice) 对产品按价格升序排序。
  • 分组 :使用 Collectors.groupingBytype 字段分组。
  • 结果collect 是一个 Map,键为产品类型(如 "电子"、"家具"),值为对应类型的产品列表。

3. 对分组后的数据排序

这一部分代码对每个分组内的产品列表进行排序,先按 seq1 正序,再按价格倒序。

java 复制代码
// 对分组后的数据按 seq1 正序和价格倒序排序
Map<String, List<Product>> sortedCollect = result.stream()
        .collect(Collectors.groupingBy(
                Product::getType, // 按类型分组
                LinkedHashMap::new, // 保持分组顺序
                Collectors.collectingAndThen(
                        Collectors.toList(), // 先收集到 List
                        list -> list.stream()
                                .sorted(Comparator.comparing(Product::getPrice).reversed()) // 价格倒序
                                .sorted(Comparator.comparing(Product::getSeq1)) // seq1 正序
                                .collect(Collectors.toList()) // 排序后收集为 List
                )
        ));
说明
  • 分组 :与上一步类似,按 type 字段分组。
  • 排序逻辑
    1. 价格倒序Comparator.comparing(Product::getPrice).reversed()
    2. seq1 正序Comparator.comparing(Product::getSeq1)
    3. 排序优先级 :先按 seq1 排序,再按价格排序。
  • 结果sortedCollect 是一个 Map,每个分组内的产品列表已按上述规则排序。

4. 对分组的键排序

这一部分代码对分组的键(即产品类型)按字典序排序。

java 复制代码
// 对分组后的 key 进行排序
Map<String, List<Product>> sortedMap2 = sortedCollect.entrySet().stream()
        .sorted(Map.Entry.comparingByKey()) // 按键(类型)排序
        .collect(Collectors.toMap(
                Map.Entry::getKey,
                Map.Entry::getValue,
                (oldVal, newVal) -> oldVal,
                LinkedHashMap::new // 保持插入顺序
        ));
说明
  • 排序逻辑Map.Entry.comparingByKey() 按键(类型)字典序排序。
  • 结果sortedMap2 是一个按类型排序的 Map,每个分组内的产品列表已按规则排序。

5. 按顺序取出每组的第 i 个元素

这一部分代码从每个分组中依次取出第 i 个元素,直到所有元素都被取出。

java 复制代码
// 遍历分组数据,按顺序取出每组的第 i 个元素
int index = 0;
boolean done = false;

while (!done) {
    done = true;
    for (String key : sortedMap2.keySet()) {
        List<Product> group = sortedMap2.get(key);
        if (index < group.size()) {
            String name = sortedMap2.get(key).get(index).name;
            System.out.println("name: " + name);
            resp.add(group.get(index)); // 添加到结果列表
            done = false; // 如果还有未取出的元素,继续循环
        }
    }
    index++;
}
说明
  • 逻辑
    1. 遍历每个分组,取出第 index 个元素。
    2. 如果某个分组的元素已取完,则跳过。
    3. 当所有分组的元素都取完时,退出循环。
  • 结果resp 列表按顺序存储所有产品。

6. 按每组的第 i 个元素重新排序

这一部分代码实现了另一种遍历方式,按每组的第 i 个元素重新排序。

java 复制代码
// 计算每组的最大长度
int count = 0;
for (String key : sortedMap2.keySet()) {
    count = Math.max(count, sortedMap2.get(key).size());
}

// 按每组的第 i 个元素重新排序
int j = 0;
for (int i = 0; i < count; i++) {
    for (String key : sortedMap2.keySet()) {
        if (i >= sortedMap2.get(key).size()) {
            continue; // 跳过超出范围的元素
        }
        String name = sortedMap2.get(key).get(i).name;
        System.out.println("name: " + name);
        resp.add(j, sortedMap2.get(key).get(i)); // 插入到结果列表
        j++;
    }
}
说明
  • 逻辑
    1. 计算每组的最大长度 count
    2. 遍历每组的第 i 个元素,按顺序插入到 resp 列表中。
  • 结果resp 列表存储按每组第 i 个元素排序的结果。

7. 产品类定义

Product 类是代码的核心数据结构,定义如下:

java 复制代码
static class Product {
    String name;
    int price;
    String type;
    int seq0;
    int seq1;

    public Product(String name, int price, String type, int seq0, int seq1) {
        this.name = name;
        this.price = price;
        this.type = type;
        this.seq0 = seq0;
        this.seq1 = seq1;
    }

    public String getName() {
        return name;
    }

    public int getPrice() {
        return price;
    }

    public String getType() {
        return type;
    }

    public int getSeq0() {
        return seq0;
    }

    public int getSeq1() {
        return seq1;
    }
}

总结

通过拆分代码,我们可以清晰地看到每个部分的功能:

  1. 数据初始化。
  2. 按价格排序并分组。
  3. 对分组内的产品排序。
  4. 对分组的键排序。
  5. 按顺序取出每组的第 i 个元素。
  6. 按每组的第 i 个元素重新排序。

如果需要进一步优化或补充说明,请随时告知!

相关推荐
shine_du8 分钟前
架构师之路--springboot核心类SpringApplication方法run的源码启动流程
java·spring boot·后端
毒丐10 分钟前
VirtualBox使用教程
java·服务器·c++
Easy_Company27 分钟前
Hadoop实验:关于MapReduce词频统计的实验步骤
java·大数据·hadoop·mapreduce
数据小爬虫@29 分钟前
如何利用Java爬虫获得淘宝买家秀
java·开发语言·爬虫
waicsdn_haha38 分钟前
MySql-9.1.0安装详细教程(保姆级)
java·数据库·后端·mysql·php·性能测试·数据库开发
Sunmanit2 小时前
异步将用户信息存入 Redis 缓存
java·数据库·redis·缓存
文浩(楠搏万)2 小时前
Tomcat HTTPS配置、域名解析及Java WAR包打包
java·运维·服务器·nginx·http·https·tomcat
KaiPeng-Nie2 小时前
代码随想录day21 | leetcode 669.修剪二叉搜索树 108.将有序数组转换为二叉搜索树 538.把二叉搜索树转换为累加树 二叉树总结篇
java·数据结构·算法·leetcode·二叉树
愿时间能学会宽恕2 小时前
SpringBoot后端开发常用工具详细介绍——Minio资源管理器
java·spring boot·后端
一只淡水鱼662 小时前
【Spring】Controller层常用注解的介绍和使用
java·后端·spring