Collection.stream()深度使用与实战

Collection.stream()是 Java 8 函数式编程的核心,它提供了声明式处理集合数据的能力。本文将深入探讨其使用场景、高级技巧和实战应用。


一、核心概念与基础

1. Stream 的特点

  • 非存储:不存储数据,只是数据的视图
  • 函数式操作:支持 Lambda 表达式和方法引用
  • 惰性求值:中间操作延迟执行
  • 可并行:支持多线程处理

2. 创建 Stream 的方式

ini 复制代码
// 从集合创建
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream1 = list.stream();

// 从数组创建
String[] array = {"a", "b", "c"};
Stream<String> stream2 = Arrays.stream(array);

// 使用 Stream.of
Stream<String> stream3 = Stream.of("a", "b", "c");

// 生成无限流
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 1);

二、核心使用场景

1. 数据过滤与转换

scss 复制代码
List<User> users = getUserList();

// 过滤+转换组合操作
List<String> names = users.stream()
    .filter(user -> user.getAge() > 18)      // 过滤成年用户
    .map(User::getName)                      // 提取姓名
    .filter(name -> name.startsWith("张"))    // 过滤姓氏
    .collect(Collectors.toList());

2. 数据统计与分析

less 复制代码
List<Order> orders = getOrderList();

// 多维度统计
Map<String, DoubleSummaryStatistics> stats = orders.stream()
    .collect(Collectors.groupingBy(
        Order::getCategory,
        Collectors.summarizingDouble(Order::getAmount)
    ));

stats.forEach((category, stat) -> {
    System.out.println(category + ": " + 
        "数量=" + stat.getCount() + 
        ", 总额=" + stat.getSum() +
        ", 平均=" + stat.getAverage());
});

3. 复杂数据分组

swift 复制代码
List<Employee> employees = getEmployees();

// 多级分组
Map<Department, Map<String, List<Employee>>> grouped = employees.stream()
    .collect(Collectors.groupingBy(
        Employee::getDepartment,
        Collectors.groupingBy(emp -> {
            if (emp.getSalary() > 10000) return "高薪";
            else if (emp.getSalary() > 5000) return "中薪";
            else return "低薪";
        })
    ));

4. 并行数据处理

ini 复制代码
List<Data> largeData = getLargeData();

// 并行处理大数据集
Map<String, List<Data>> result = largeData.parallelStream()
    .filter(data -> data.isValid())
    .collect(Collectors.groupingByConcurrent(Data::getType));

三、深度使用技巧

1. 自定义 Collector

typescript 复制代码
// 自定义统计收集器
public class CustomStatsCollector implements Collector<Integer, int[], CustomStats> {
    @Override
    public Supplier<int[]> supplier() {
        return () -> new int[4]; // [count, sum, min, max]
    }

    @Override
    public BiConsumer<int[], Integer> accumulator() {
        return (arr, num) -> {
            arr[0]++;           // count
            arr[1] += num;      // sum
            arr[2] = Math.min(arr[2], num); // min
            arr[3] = Math.max(arr[3], num); // max
        };
    }

    @Override
    public BinaryOperator<int[]> combiner() {
        return (arr1, arr2) -> new int[]{
            arr1[0] + arr2[0],
            arr1[1] + arr2[1],
            Math.min(arr1[2], arr2[2]),
            Math.max(arr1[3], arr2[3])
        };
    }

    @Override
    public Function<int[], CustomStats> finisher() {
        return arr -> new CustomStats(arr[0], arr[1], arr[2], arr[3]);
    }

    @Override
    public Set<Characteristics> characteristics() {
        return Set.of(Characteristics.CONCURRENT);
    }
}

2. 异常处理技巧

rust 复制代码
List<String> numbers = Arrays.asList("1", "2", "abc", "4");

// 安全的数值转换
List<Integer> result = numbers.stream()
    .map(str -> {
        try {
            return Integer.parseInt(str);
        } catch (NumberFormatException e) {
            return null; // 或者使用 Optional
        }
    })
    .filter(Objects::nonNull)
    .collect(Collectors.toList());

3. 性能优化模式

scss 复制代码
// 使用 primitive stream 避免装箱开销
IntStream intStream = list.stream().mapToInt(Integer::intValue);

// 批量处理避免多次流操作
public void processInBatches(List<Data> dataList, int batchSize) {
    IntStream.range(0, (dataList.size() + batchSize - 1) / batchSize)
        .mapToObj(i -> dataList.subList(i * batchSize, 
            Math.min((i + 1) * batchSize, dataList.size())))
        .parallel()
        .forEach(this::processBatch);
}

四、实战案例

案例1:电商订单分析

less 复制代码
public class OrderAnalyzer {
    public void analyzeOrders(List<Order> orders) {
        // 1. 销售额统计
        Map<String, Double> salesByCategory = orders.stream()
            .collect(Collectors.groupingBy(
                Order::getCategory,
                Collectors.summingDouble(Order::getTotalAmount)
            ));

        // 2. 热销商品排名
        Map<String, Long> productRank = orders.stream()
            .flatMap(order -> order.getItems().stream())
            .collect(Collectors.groupingBy(
                OrderItem::getProductId,
                Collectors.summingLong(OrderItem::getQuantity)
            ))
            .entrySet().stream()
            .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
            .limit(10)
            .collect(Collectors.toMap(
                Map.Entry::getKey,
                Map.Entry::getValue,
                (e1, e2) -> e1,
                LinkedHashMap::new
            ));

        // 3. 用户购买行为分析
        Map<String, UserBehavior> userBehavior = orders.stream()
            .collect(Collectors.groupingBy(
                Order::getUserId,
                Collectors.collectingAndThen(
                    Collectors.toList(),
                    userOrders -> new UserBehavior(
                        userOrders.size(),
                        userOrders.stream().mapToDouble(Order::getTotalAmount).sum(),
                        userOrders.stream().mapToDouble(Order::getTotalAmount).average().orElse(0)
                    )
                )
            ));
    }
}

案例2:日志分析系统

typescript 复制代码
public class LogAnalyzer {
    public void analyzeLogs(List<LogEntry> logs) {
        // 错误日志统计
        Map<String, Map<String, Long>> errorStats = logs.stream()
            .filter(log -> log.getLevel().equals("ERROR"))
            .collect(Collectors.groupingBy(
                LogEntry::getService,
                Collectors.groupingBy(
                    LogEntry::getErrorCode,
                    Collectors.counting()
                )
            ));

        // 性能监控
        Map<String, DoubleSummaryStatistics> performanceStats = logs.stream()
            .filter(log -> log.getResponseTime() > 0)
            .collect(Collectors.groupingBy(
                LogEntry::getEndpoint,
                Collectors.summarizingDouble(LogEntry::getResponseTime)
            ));

        // 异常检测
        List<LogEntry> anomalies = logs.stream()
            .filter(log -> isAnomaly(log, performanceStats))
            .collect(Collectors.toList());
    }

    private boolean isAnomaly(LogEntry log, Map<String, DoubleSummaryStatistics> stats) {
        DoubleSummaryStatistics stat = stats.get(log.getEndpoint());
        return stat != null && log.getResponseTime() > stat.getAverage() + 3 * stat.getStandardDeviation();
    }
}

案例3:社交网络分析

typescript 复制代码
public class SocialNetworkAnalyzer {
    public void analyzeNetwork(List<User> users) {
        // 用户关系网络
        Map<String, List<String>> socialGraph = users.stream()
            .collect(Collectors.toMap(
                User::getId,
                User::getFriends
            ));

        // 影响力分析
        Map<String, Integer> influenceScores = users.stream()
            .collect(Collectors.toMap(
                User::getId,
                user -> calculateInfluenceScore(user, socialGraph)
            ));

        // 社区发现
        List<Set<String>> communities = detectCommunities(socialGraph);
    }

    private int calculateInfluenceScore(User user, Map<String, List<String>> graph) {
        return graph.get(user.getId()).stream()
            .mapToInt(friendId -> graph.get(friendId).size())
            .sum();
    }
}

五、最佳实践与注意事项

1. 性能考虑

scss 复制代码
// 好的实践:使用 primitive streams
IntStream intStream = list.stream().mapToInt(Integer::intValue);

// 避免在流中执行耗时操作
List<Result> results = dataList.stream()
    .map(data -> {
        // 避免:每个元素都进行网络请求或数据库操作
        return expensiveOperation(data);
    })
    .collect(Collectors.toList());

// 改为:批量处理或使用并行流
List<Result> results = dataList.parallelStream()
    .map(this::expensiveOperation)
    .collect(Collectors.toList());

2. 可读性维护

scss 复制代码
// 使用方法引用提升可读性
List<String> names = users.stream()
    .filter(User::isActive)
    .map(User::getFullName)
    .collect(Collectors.toList());

// 复杂操作提取为方法
List<Report> reports = data.stream()
    .map(this::transformToReport)
    .filter(this::validateReport)
    .collect(Collectors.toList());

3. 错误处理

kotlin 复制代码
// 统一的异常处理策略
public <T, R> List<R> safeStreamProcess(List<T> list, Function<T, R> mapper) {
    return list.stream()
        .map(item -> {
            try {
                return mapper.apply(item);
            } catch (Exception e) {
                log.error("Processing failed for item: {}", item, e);
                return null;
            }
        })
        .filter(Objects::nonNull)
        .collect(Collectors.toList());
}

六、总结

适用场景

  • 数据转换和过滤
  • 统计分析和聚合
  • 复杂数据分组
  • 并行数据处理
  • 声明式编程场景

不适用场景

  • 需要索引访问的复杂算法
  • 频繁修改集合内容的操作
  • 极度性能敏感的场景(需要手动优化)​
  • 复杂的异常处理流程

性能建议

  1. 小数据集(<1000):顺序流足够
  2. 中等数据集(1000-10000):根据操作复杂度选择
  3. 大数据集(>10000):优先考虑并行流
  4. IO密集型:谨慎使用并行流(可能适得其反)
相关推荐
桦说编程11 天前
Java 要变天了,支持类型类 type classes
java·后端·函数式编程
楽码14 天前
傻傻分不清:信息和通信复杂度
网络协议·算法·函数式编程
桦说编程17 天前
方法一定要有返回值 \ o /
java·后端·函数式编程
桦说编程18 天前
Java 中如何创建不可变类型
java·后端·函数式编程
桦说编程1 个月前
CompletableFuture 的第四种调用模式
java·性能优化·函数式编程
songgeb2 个月前
Currying and Partial application
swift·函数式编程
桦说编程3 个月前
写时复制COW核心原理解读
java·性能优化·函数式编程
桦说编程3 个月前
配置快照实现之持久化数据结构
java·后端·函数式编程
石金龙3 个月前
用 Ramda 做简易日历
函数式编程·ramda