本文目录导读:

我来详细介绍Java中几种常见的数据聚合实现方案。
使用Stream API进行聚合
基础数据模型
public class Order {
private Long id;
private String category;
private String product;
private double amount;
private LocalDate orderDate;
private String status;
// 构造函数、getter/setter省略
public Order(Long id, String category, String product, double amount, LocalDate orderDate, String status) {
this.id = id;
this.category = category;
this.product = product;
this.amount = amount;
this.orderDate = orderDate;
this.status = status;
}
// getter方法
public String getCategory() { return category; }
public String getProduct() { return product; }
public double getAmount() { return amount; }
public LocalDate getOrderDate() { return orderDate; }
public String getStatus() { return status; }
}
示例数据
public class DataGenerator {
public static List<Order> generateOrders() {
return Arrays.asList(
new Order(1L, "电子产品", "手机", 5000.0, LocalDate.of(2024, 1, 15), "已完成"),
new Order(2L, "电子产品", "电脑", 8000.0, LocalDate.of(2024, 1, 20), "已完成"),
new Order(3L, "食品", "水果", 200.0, LocalDate.of(2024, 2, 5), "已完成"),
new Order(4L, "食品", "零食", 150.0, LocalDate.of(2024, 2, 10), "已完成"),
new Order(5L, "电子产品", "手机", 5200.0, LocalDate.of(2024, 2, 15), "已完成"),
new Order(6L, "服装", "T恤", 200.0, LocalDate.of(2024, 3, 1), "已取消"),
new Order(7L, "服装", "裤子", 300.0, LocalDate.of(2024, 3, 10), "已完成"),
new Order(8L, "电子产品", "平板", 3500.0, LocalDate.of(2024, 3, 15), "已完成")
);
}
}
1 按类别分组统计销售总额
public class CategoryAggregationDemo {
public static void main(String[] args) {
List<Order> orders = DataGenerator.generateOrders();
// 按类别分组统计销售额
Map<String, Double> categorySales = orders.stream()
.filter(o -> "已完成".equals(o.getStatus())) // 只统计已完成订单
.collect(Collectors.groupingBy(
Order::getCategory,
Collectors.summingDouble(Order::getAmount)
));
System.out.println("各品类销售额:");
categorySales.forEach((category, total) ->
System.out.printf("%s: %.2f元\n", category, total));
}
}
2 多维度聚合统计
public class MultiDimensionAggregationDemo {
public static void main(String[] args) {
List<Order> orders = DataGenerator.generateOrders();
// 按月份和类别统计
Map<Object, Map<String, Double>> monthlyCategorySales = orders.stream()
.filter(o -> "已完成".equals(o.getStatus()))
.collect(Collectors.groupingBy(
order -> order.getOrderDate().getMonth(),
Collectors.groupingBy(
Order::getCategory,
Collectors.summingDouble(Order::getAmount)
)
));
System.out.println("月度品类销售统计:");
monthlyCategorySales.forEach((month, categoryMap) -> {
System.out.println(month + ":");
categoryMap.forEach((category, total) ->
System.out.printf(" %s: %.2f元\n", category, total));
});
}
}
3 复杂的聚合统计
public class ComplexAggregationDemo {
public static void main(String[] args) {
List<Order> orders = DataGenerator.generateOrders();
// 聚合统计结果类
class CategoryStats {
long count;
double totalAmount;
double avgAmount;
double maxAmount;
double minAmount;
@Override
public String toString() {
return String.format("订单数=%d, 总额=%.2f, 均价=%.2f, 最高=%.2f, 最低=%.2f",
count, totalAmount, avgAmount, maxAmount, minAmount);
}
}
// 按类别进行综合统计
Map<String, CategoryStats> stats = orders.stream()
.filter(o -> "已完成".equals(o.getStatus()))
.collect(Collectors.groupingBy(
Order::getCategory,
Collectors.collectingAndThen(
Collectors.toList(),
list -> {
CategoryStats s = new CategoryStats();
s.count = list.size();
s.totalAmount = list.stream().mapToDouble(Order::getAmount).sum();
s.avgAmount = list.stream().mapToDouble(Order::getAmount).average().orElse(0);
s.maxAmount = list.stream().mapToDouble(Order::getAmount).max().orElse(0);
s.minAmount = list.stream().mapToDouble(Order::getAmount).min().orElse(0);
return s;
}
)
));
System.out.println("各品类详细统计:");
stats.forEach((category, stat) ->
System.out.println(category + ": " + stat));
}
}
使用数据库SQL进行聚合
1 实体类和Repository
@Entity
@Table(name = "orders")
public class OrderEntity {
@Id
private Long id;
@Column(name = "category")
private String category;
@Column(name = "product")
private String product;
@Column(name = "amount")
private Double amount;
@Column(name = "order_date")
private LocalDate orderDate;
@Column(name = "status")
private String status;
// getter/setter省略
}
// JPA Repository
@Repository
public interface OrderRepository extends JpaRepository<OrderEntity, Long> {
// 按类别统计销售额
@Query("SELECT o.category, SUM(o.amount) FROM OrderEntity o " +
"WHERE o.status = '已完成' GROUP BY o.category")
List<Object[]> findCategorySales();
// 按月统计销售额
@Query("SELECT FUNCTION('MONTH', o.orderDate), SUM(o.amount) " +
"FROM OrderEntity o WHERE o.status = '已完成' " +
"GROUP BY FUNCTION('MONTH', o.orderDate)")
List<Object[]> findMonthlySales();
// 多维度统计
@Query("SELECT FUNCTION('MONTH', o.orderDate), o.category, " +
"COUNT(o), SUM(o.amount), AVG(o.amount) " +
"FROM OrderEntity o WHERE o.status = '已完成' " +
"GROUP BY FUNCTION('MONTH', o.orderDate), o.category")
List<Object[]> findMonthlyCategoryStats();
}
2 聚合结果映射
// 聚合结果DTO
public class SalesStatDTO {
private String category;
private Double totalAmount;
private Long orderCount;
// 构造函数、getter/setter
public SalesStatDTO(String category, Double totalAmount, Long orderCount) {
this.category = category;
this.totalAmount = totalAmount;
this.orderCount = orderCount;
}
}
// 服务层
@Service
public class OrderAggregationService {
@Autowired
private OrderRepository orderRepository;
public List<SalesStatDTO> getCategorySales() {
List<Object[]> results = orderRepository.findCategorySales();
return results.stream()
.map(row -> new SalesStatDTO(
(String) row[0],
(Double) row[1],
null
))
.collect(Collectors.toList());
}
}
使用MapReduce思维进行聚合
public class MapReduceAggregationDemo {
public static void main(String[] args) {
List<Order> orders = DataGenerator.generateOrders();
// Map阶段:提取键值对
Map<String, Double> categoryAmounts = orders.stream()
.filter(o -> "已完成".equals(o.getStatus()))
.collect(Collectors.toMap(
Order::getCategory,
Order::getAmount,
Double::sum // Reduce阶段:合并
));
System.out.println("MapReduce方式统计结果:");
categoryAmounts.forEach((category, total) ->
System.out.printf("%s: %.2f元\n", category, total));
}
}
使用并发聚合提升性能
public class ParallelAggregationDemo {
public static void main(String[] args) {
List<Order> orders = DataGenerator.generateOrders();
// 使用并行流处理大数据集
Map<String, Double> categorySales = orders.parallelStream()
.filter(o -> "已完成".equals(o.getStatus()))
.collect(Collectors.groupingByConcurrent(
Order::getCategory,
Collectors.summingDouble(Order::getAmount)
));
System.out.println("并行聚合结果:");
categorySales.forEach((category, total) ->
System.out.printf("%s: %.2f元\n", category, total));
}
}
自定义聚合器
public class CustomAggregatorDemo {
// 自定义聚合结果类
static class ProductSalesStats {
private String product;
private int orderCount;
private double totalAmount;
private double maxAmount;
private LocalDate lastOrderDate;
}
// 自定义Collector
static class ProductStatsCollector
implements Collector<Order, Map<String, ProductSalesStats>, List<ProductSalesStats>> {
@Override
public Supplier<Map<String, ProductSalesStats>> supplier() {
return HashMap::new;
}
@Override
public BiConsumer<Map<String, ProductSalesStats>, Order> accumulator() {
return (map, order) -> {
map.computeIfAbsent(order.getProduct(), k -> new ProductSalesStats());
ProductSalesStats stats = map.get(order.getProduct());
stats.product = order.getProduct();
stats.orderCount++;
stats.totalAmount += order.getAmount();
stats.maxAmount = Math.max(stats.maxAmount, order.getAmount());
if (stats.lastOrderDate == null ||
order.getOrderDate().isAfter(stats.lastOrderDate)) {
stats.lastOrderDate = order.getOrderDate();
}
};
}
@Override
public BinaryOperator<Map<String, ProductSalesStats>> combiner() {
return (map1, map2) -> {
map2.forEach((key, value) -> {
map1.merge(key, value, (v1, v2) -> {
v1.orderCount += v2.orderCount;
v1.totalAmount += v2.totalAmount;
v1.maxAmount = Math.max(v1.maxAmount, v2.maxAmount);
if (v2.lastOrderDate.isAfter(v1.lastOrderDate)) {
v1.lastOrderDate = v2.lastOrderDate;
}
return v1;
});
});
return map1;
};
}
@Override
public Function<Map<String, ProductSalesStats>, List<ProductSalesStats>> finisher() {
return map -> new ArrayList<>(map.values());
}
@Override
public Set<Characteristics> characteristics() {
return Collections.unmodifiableSet(EnumSet.of(Characteristics.UNORDERED));
}
}
public static void main(String[] args) {
List<Order> orders = DataGenerator.generateOrders();
List<ProductSalesStats> stats = orders.stream()
.filter(o -> "已完成".equals(o.getStatus()))
.collect(new ProductStatsCollector());
System.out.println("产品统计结果:");
stats.forEach(s -> System.out.printf(
"商品=%s, 订单数=%d, 总额=%.2f, 最高价=%.2f, 最后下单=%s\n",
s.product, s.orderCount, s.totalAmount,
s.maxAmount, s.lastOrderDate));
}
}
实战建议
-
选择合适的聚合方式:
- 小数据集:使用Stream API
- 大数据集:使用数据库SQL或并行流
- 实时计算:使用MapReduce思想
-
性能优化:
- 使用
parallelStream()处理大数据集 - 避免在聚合过程中创建临时对象
- 合理设置JVM内存参数
- 使用
-
代码维护性:
- 封装聚合逻辑到独立方法
- 使用DTO作为聚合结果
- 编写单元测试验证聚合结果
这些示例涵盖了Java中常见的数据聚合实现方式,可以根据实际需求选择合适的方案。