Java Stream 实际用法详解

一、Stream 基本概念

1.1 Stream 是什么

Java Stream 是 Java 8 引入的处理集合数据的 API,支持声明式编程和函数式操作。

// 传统方式 vs Stream 方式
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// 传统方式
List<String> result = new ArrayList<>();
for (String name : names) {
    if (name.startsWith("A")) {
        result.add(name.toUpperCase());
    }
}

// Stream 方式
List<String> result = names.stream()
    .filter(name -> name.startsWith("A"))
    .map(String::toUpperCase)
    .collect(Collectors.toList());

二、Stream 创建方式

2.1 从集合创建

// 从 List 创建
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream1 = list.stream();
Stream<String> parallelStream = list.parallelStream();

// 从 Set 创建
Set<Integer> set = new HashSet<>(Arrays.asList(1, 2, 3));
Stream<Integer> stream2 = set.stream();

2.2 从数组创建

String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);

// 指定范围
Stream<String> rangeStream = Arrays.stream(array, 1, 3); // "b", "c"

2.3 使用 Stream.of()

Stream<String> stream = Stream.of("a", "b", "c");
Stream<Integer> intStream = Stream.of(1, 2, 3);

2.4 使用 Builder

Stream<String> stream = Stream.<String>builder()
    .add("a")
    .add("b")
    .add("c")
    .build();

2.5 生成无限流

// 生成无限序列
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 2)
    .limit(10); // 限制为前10个

// 生成随机数
Stream<Double> randomStream = Stream.generate(Math::random)
    .limit(5);

2.6 基本类型 Stream

// IntStream
IntStream intStream = IntStream.range(1, 10); // 1-9
IntStream closedIntStream = IntStream.rangeClosed(1, 10); // 1-10

// LongStream
LongStream longStream = LongStream.range(1, 100);

// DoubleStream
DoubleStream doubleStream = DoubleStream.of(1.0, 2.0, 3.0);

三、中间操作(Intermediate Operations)

3.1 filter() - 过滤

List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");

// 过滤长度大于3的名字
List<String> filtered = names.stream()
    .filter(name -> name.length() > 3)
    .collect(Collectors.toList()); // [Alice, Charlie, David]

// 多条件过滤
List<String> multiFiltered = names.stream()
    .filter(name -> name.length() > 3)
    .filter(name -> name.startsWith("A"))
    .collect(Collectors.toList()); // [Alice]

3.2 map() - 映射转换

// 转换大小写
List<String> upperCaseNames = names.stream()
    .map(String::toUpperCase)
    .collect(Collectors.toList());

// 提取对象属性
class Person {
    private String name;
    private int age;
    
    // getters, setters
}

List<Person> persons = Arrays.asList(
    new Person("Alice", 25),
    new Person("Bob", 30)
);

List<String> personNames = persons.stream()
    .map(Person::getName)
    .collect(Collectors.toList());

// 复杂转换
List<Integer> nameLengths = names.stream()
    .map(String::length)
    .collect(Collectors.toList());

3.3 flatMap() - 扁平化映射

// 将多个集合合并为一个流
List<List<String>> nestedList = Arrays.asList(
    Arrays.asList("a", "b"),
    Arrays.asList("c", "d")
);

List<String> flatList = nestedList.stream()
    .flatMap(Collection::stream)
    .collect(Collectors.toList()); // [a, b, c, d]

// 处理嵌套对象
class Order {
    private List<Item> items;
    // getter, setter
}

class Item {
    private String name;
    private double price;
    // getter, setter
}

List<Order> orders = Arrays.asList(order1, order2);
List<Item> allItems = orders.stream()
    .flatMap(order -> order.getItems().stream())
    .collect(Collectors.toList());

3.4 distinct() - 去重

List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3, 4);
List<Integer> distinctNumbers = numbers.stream()
    .distinct()
    .collect(Collectors.toList()); // [1, 2, 3, 4]

3.5 sorted() - 排序

// 自然排序
List<String> sortedNames = names.stream()
    .sorted()
    .collect(Collectors.toList());

// 自定义排序
List<Person> sortedByAge = persons.stream()
    .sorted(Comparator.comparingInt(Person::getAge))
    .collect(Collectors.toList());

// 多重排序
List<Person> multiSorted = persons.stream()
    .sorted(Comparator.comparing(Person::getName)
        .thenComparing(Person::getAge))
    .collect(Collectors.toList());

// 反向排序
List<Person> reverseSorted = persons.stream()
    .sorted(Comparator.comparing(Person::getAge).reversed())
    .collect(Collectors.toList());

3.6 limit() 和 skip() - 限制和跳过

List<Integer> numbers = IntStream.rangeClosed(1, 10)
    .boxed()
    .collect(Collectors.toList());

// 获取前5个
List<Integer> firstFive = numbers.stream()
    .limit(5)
    .collect(Collectors.toList()); // [1, 2, 3, 4, 5]

// 跳过前3个
List<Integer> skipThree = numbers.stream()
    .skip(3)
    .collect(Collectors.toList()); // [4, 5, 6, 7, 8, 9, 10]

// 分页实现
int pageSize = 3;
int pageNum = 2; // 第二页
List<Integer> page = numbers.stream()
    .skip((pageNum - 1) * pageSize)
    .limit(pageSize)
    .collect(Collectors.toList()); // [4, 5, 6]

3.7 peek() - 调试查看

List<String> result = names.stream()
    .peek(System.out::println) // 调试输出
    .filter(name -> name.length() > 3)
    .peek(name -> System.out.println("Filtered: " + name))
    .collect(Collectors.toList());

四、终端操作(Terminal Operations)

4.1 forEach() - 遍历

// 遍历输出
names.stream().forEach(System.out::println);

// 并行流遍历(无序)
names.parallelStream().forEach(System.out::println);

// 并行流有序遍历
names.parallelStream().forEachOrdered(System.out::println);

4.2 collect() - 收集

// 收集到List
List<String> list = names.stream()
    .filter(n -> n.length() > 3)
    .collect(Collectors.toList());

// 收集到Set(自动去重)
Set<String> set = names.stream()
    .collect(Collectors.toSet());

// 收集到特定集合
TreeSet<String> treeSet = names.stream()
    .collect(Collectors.toCollection(TreeSet::new));

// 收集到Map
Map<String, Integer> nameLengthMap = names.stream()
    .collect(Collectors.toMap(
        Function.identity(), // key: 名字本身
        String::length       // value: 名字长度
    ));

// 处理key冲突
Map<String, Integer> mapWithConflict = names.stream()
    .collect(Collectors.toMap(
        Function.identity(),
        String::length,
        (oldValue, newValue) -> oldValue // 冲突时保留旧值
    ));

4.3 toArray() - 转换为数组

String[] array = names.stream().toArray(String[]::new);

// 基本类型数组
int[] intArray = IntStream.range(1, 5).toArray();

4.4 reduce() - 归约

// 求和
Optional<Integer> sum = Stream.of(1, 2, 3, 4, 5)
    .reduce(Integer::sum);
// 或
int sumResult = Stream.of(1, 2, 3, 4, 5)
    .reduce(0, Integer::sum);

// 求最大值
Optional<Integer> max = Stream.of(1, 2, 3, 4, 5)
    .reduce(Integer::max);

// 字符串连接
String concatenated = Stream.of("a", "b", "c")
    .reduce("", String::concat);

// 复杂对象归约
class Order {
    private List<Item> items;
    // getter, setter
}

double totalPrice = orders.stream()
    .flatMap(order -> order.getItems().stream())
    .map(Item::getPrice)
    .reduce(0.0, Double::sum);

4.5 匹配操作

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// 任意匹配
boolean anyMatch = numbers.stream()
    .anyMatch(n -> n > 3); // true

// 全部匹配
boolean allMatch = numbers.stream()
    .allMatch(n -> n > 0); // true

// 无匹配
boolean noneMatch = numbers.stream()
    .noneMatch(n -> n < 0); // true

4.6 查找操作

Optional<String> first = names.stream()
    .filter(n -> n.startsWith("A"))
    .findFirst(); // 返回第一个元素

Optional<String> any = names.stream()
    .filter(n -> n.startsWith("A"))
    .findAny(); // 并行流中效率更高

4.7 统计操作

// 计数
long count = names.stream().count();

// 使用 IntStream 统计
IntSummaryStatistics stats = IntStream.range(1, 100)
    .summaryStatistics();

System.out.println("Count: " + stats.getCount());
System.out.println("Sum: " + stats.getSum());
System.out.println("Min: " + stats.getMin());
System.out.println("Max: " + stats.getMax());
System.out.println("Average: " + stats.getAverage());

五、Collectors 高级用法

5.1 分组

List<Person> persons = Arrays.asList(
    new Person("Alice", 25, "New York"),
    new Person("Bob", 30, "New York"),
    new Person("Charlie", 25, "London")
);

// 按年龄分组
Map<Integer, List<Person>> groupByAge = persons.stream()
    .collect(Collectors.groupingBy(Person::getAge));

// 多级分组
Map<Integer, Map<String, List<Person>>> multiGroup = persons.stream()
    .collect(Collectors.groupingBy(
        Person::getAge,
        Collectors.groupingBy(Person::getCity)
    ));

// 分组并计数
Map<Integer, Long> ageCount = persons.stream()
    .collect(Collectors.groupingBy(
        Person::getAge,
        Collectors.counting()
    ));

// 分组并求平均值
Map<Integer, Double> averageAgeByCity = persons.stream()
    .collect(Collectors.groupingBy(
        Person::getAge,
        Collectors.averagingInt(Person::getAge)
    ));

5.2 分区

// 按条件分区
Map<Boolean, List<Person>> partition = persons.stream()
    .collect(Collectors.partitioningBy(p -> p.getAge() > 25));

// 分区并统计
Map<Boolean, Long> partitionCount = persons.stream()
    .collect(Collectors.partitioningBy(
        p -> p.getAge() > 25,
        Collectors.counting()
    ));

5.3 连接字符串

// 简单连接
String joined = names.stream()
    .collect(Collectors.joining()); // "AliceBobCharlie"

// 带分隔符
String joinedWithComma = names.stream()
    .collect(Collectors.joining(", ")); // "Alice, Bob, Charlie"

// 带前缀和后缀
String joinedFull = names.stream()
    .collect(Collectors.joining(", ", "[", "]"));
// "[Alice, Bob, Charlie]"

5.4 统计汇总

// 求总和
int totalAge = persons.stream()
    .collect(Collectors.summingInt(Person::getAge));

// 求平均值
double averageAge = persons.stream()
    .collect(Collectors.averagingInt(Person::getAge));

// 综合统计
IntSummaryStatistics ageStats = persons.stream()
    .collect(Collectors.summarizingInt(Person::getAge));

六、并行流(Parallel Stream)

6.1 创建并行流

// 从集合创建并行流
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Stream<String> parallelStream = names.parallelStream();

// 将顺序流转为并行流
Stream<String> parallel = names.stream().parallel();

// 将并行流转为顺序流
Stream<String> sequential = parallelStream.sequential();

6.2 并行流注意事项

// 非线程安全操作(错误示例)
List<Integer> unsafeList = new ArrayList<>();
IntStream.range(0, 10000).parallel()
    .forEach(unsafeList::add); // 可能丢失数据

// 线程安全操作(正确示例)
List<Integer> safeList = IntStream.range(0, 10000).parallel()
    .boxed()
    .collect(Collectors.toList());

// 并行流排序
List<Integer> sortedParallel = IntStream.range(0, 10000).parallel()
    .sorted()
    .boxed()
    .collect(Collectors.toList());

6.3 并行流性能考虑

// 小数据量顺序流更快
List<Integer> smallList = IntStream.range(0, 100)
    .boxed().collect(Collectors.toList());

long start = System.currentTimeMillis();
smallList.stream().map(i -> i * 2).count();
long sequentialTime = System.currentTimeMillis() - start;

start = System.currentTimeMillis();
smallList.parallelStream().map(i -> i * 2).count();
long parallelTime = System.currentTimeMillis() - start;

System.out.println("Sequential: " + sequentialTime + "ms");
System.out.println("Parallel: " + parallelTime + "ms");

七、实战案例

7.1 数据处理管道

public class StreamPipelineExample {
    public static void main(String[] args) {
        List<Transaction> transactions = Arrays.asList(
            new Transaction("USD", 1000.0, Transaction.Type.DEPOSIT),
            new Transaction("USD", 500.0, Transaction.Type.WITHDRAWAL),
            new Transaction("EUR", 800.0, Transaction.Type.DEPOSIT),
            new Transaction("USD", 300.0, Transaction.Type.DEPOSIT),
            new Transaction("JPY", 10000.0, Transaction.Type.WITHDRAWAL)
        );

        // 复杂数据处理管道
        Map<String, Double> totalDepositsByCurrency = transactions.stream()
            .filter(t -> t.getType() == Transaction.Type.DEPOSIT)
            .filter(t -> t.getAmount() > 200)
            .collect(Collectors.groupingBy(
                Transaction::getCurrency,
                Collectors.summingDouble(Transaction::getAmount)
            ));

        totalDepositsByCurrency.forEach((currency, total) ->
            System.out.println(currency + ": " + total));
    }
}

7.2 文件处理

public class FileStreamExample {
    public static void main(String[] args) throws IOException {
        // 读取文件并处理
        List<String> lines = Files.lines(Paths.get("data.txt"))
            .filter(line -> !line.trim().isEmpty())
            .map(String::toUpperCase)
            .sorted()
            .collect(Collectors.toList());

        // 写入文件
        Files.write(Paths.get("output.txt"), lines);
    }
}

7.3 数据库查询模拟

public class DatabaseStreamExample {
    public static List<User> findActiveUsers(List<User> users) {
        return users.stream()
            .filter(User::isActive)
            .filter(user -> user.getLastLogin().isAfter(LocalDate.now().minusDays(30)))
            .sorted(Comparator.comparing(User::getLastLogin).reversed())
            .collect(Collectors.toList());
    }
}

八、性能优化建议

8.1 使用基本类型流

// 避免装箱拆箱开销
// 不好
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
    .mapToInt(Integer::intValue) // 需要拆箱
    .sum();

// 好
int sum = IntStream.rangeClosed(1, 5).sum();

8.2 短路操作优化

// 使用短路操作提前结束
boolean hasMatch = largeList.stream()
    .filter(this::expensiveOperation)
    .anyMatch(this::condition); // 找到第一个匹配就停止

8.3 避免重复计算

// 不好 - 重复计算
List<String> result = names.stream()
    .filter(name -> expensiveCheck(name))
    .map(name -> transform(name))
    .filter(transformed -> expensiveCheck(transformed))
    .collect(Collectors.toList());

// 好 - 缓存中间结果
List<String> filtered = names.stream()
    .filter(name -> expensiveCheck(name))
    .collect(Collectors.toList());

List<String> result = filtered.stream()
    .map(name -> transform(name))
    .filter(transformed -> expensiveCheck(transformed))
    .collect(Collectors.toList());

九、常见陷阱

9.1 Stream 只能消费一次

Stream<String> stream = names.stream();
stream.forEach(System.out::println);
// stream.count(); // 错误!Stream 已经关闭

9.2 空指针处理

// 使用 Optional 避免空指针
Optional<String> first = names.stream()
    .filter(Objects::nonNull)
    .findFirst();

// 或者在创建 Stream 时处理
Stream<String> safeStream = Optional.ofNullable(names)
    .orElse(Collections.emptyList())
    .stream();

9.3 修改外部状态

// 避免副作用
List<String> result = new ArrayList<>();
names.stream()
    .forEach(result::add); // 不好

// 使用 collect
List<String> goodResult = names.stream()
    .collect(Collectors.toList()); // 好

总结

Java Stream 提供了强大、灵活的数据处理能力,核心要点:

  1. 创建流:多种创建方式,灵活选择

  2. 中间操作:链式调用,延迟执行

  3. 终端操作:触发计算,产生结果

  4. 并行流:合理使用提升性能

  5. Collectors:强大的结果收集工具

在实际开发中,应根据具体场景选择合适的流操作,注意性能优化,避免常见陷阱。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值