阿里Java面试被问:.Java 8中Stream API的常用操作和性能考量

一、Stream API 核心概念

1. Stream 的特性

java

复制

下载

复制代码
// Stream 的本质:数据流的高级抽象
// 特点:不存储数据、惰性求值、可消费一次、支持并行

// 创建Stream的几种方式
Stream<String> stream1 = list.stream();           // 集合
Stream<String> stream2 = Stream.of("a", "b", "c"); // 值
Stream<String> stream3 = Arrays.stream(array);    // 数组
Stream<Integer> stream4 = Stream.iterate(0, n -> n + 1); // 无限流
Stream<Double> stream5 = Stream.generate(Math::random);  // 生成流
IntStream intStream = IntStream.range(1, 100);    // 数值流

二、常用操作分类

1. 中间操作(Intermediate Operations)

🔧 筛选与切片

java

复制

下载

复制代码
// filter - 过滤
List<User> adults = users.stream()
    .filter(user -> user.getAge() >= 18)
    .collect(Collectors.toList());

// distinct - 去重(基于equals和hashCode)
List<Integer> distinctNumbers = numbers.stream()
    .distinct()
    .collect(Collectors.toList());

// limit - 限制数量
List<String> first10 = list.stream()
    .limit(10)
    .collect(Collectors.toList());

// skip - 跳过元素
List<String> after5th = list.stream()
    .skip(5)
    .collect(Collectors.toList());

// takeWhile - Java 9+ (满足条件时取元素)
List<Integer> lessThan5 = numbers.stream()
    .takeWhile(n -> n < 5)
    .collect(Collectors.toList());

// dropWhile - Java 9+ (满足条件时丢弃元素)
List<Integer> afterNegative = numbers.stream()
    .dropWhile(n -> n < 0)
    .collect(Collectors.toList());
🔄 映射操作

java

复制

下载

复制代码
// map - 一对一转换
List<String> names = users.stream()
    .map(User::getName)
    .collect(Collectors.toList());

// flatMap - 一对多展开
List<String> allTags = articles.stream()
    .flatMap(article -> article.getTags().stream())
    .distinct()
    .collect(Collectors.toList());

// mapToInt/mapToLong/mapToDouble - 转换为数值流
int totalAge = users.stream()
    .mapToInt(User::getAge)
    .sum();
📊 排序操作

java

复制

下载

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

// sorted(Comparator) - 自定义排序
List<User> sortedUsers = users.stream()
    .sorted(Comparator.comparing(User::getAge)
                      .thenComparing(User::getName))
    .collect(Collectors.toList());

// 复杂排序示例
List<Employee> employees = empList.stream()
    .sorted(Comparator.comparing(Employee::getDepartment)
                      .thenComparing(Employee::getSalary, Comparator.reverseOrder())
                      .thenComparing(Employee::getName))
    .collect(Collectors.toList());

2. 终端操作(Terminal Operations)

🔍 匹配与查找

java

复制

下载

复制代码
// allMatch - 所有元素匹配
boolean allAdults = users.stream()
    .allMatch(user -> user.getAge() >= 18);

// anyMatch - 任一元素匹配
boolean hasAdmin = users.stream()
    .anyMatch(user -> user.getRole().equals("ADMIN"));

// noneMatch - 没有元素匹配
boolean noMinors = users.stream()
    .noneMatch(user -> user.getAge() < 18);

// findFirst - 返回第一个元素
Optional<User> firstUser = users.stream()
    .findFirst();

// findAny - 返回任意元素(并行流中更高效)
Optional<User> anyUser = users.parallelStream()
    .findAny();

// count - 计数
long adultCount = users.stream()
    .filter(user -> user.getAge() >= 18)
    .count();

// max/min - 最大/最小值
Optional<User> oldestUser = users.stream()
    .max(Comparator.comparing(User::getAge));

Optional<Integer> minValue = numbers.stream()
    .min(Integer::compareTo);
📦 归约与收集

java

复制

下载

复制代码
// reduce - 归约操作
// 形式1:BinaryOperator
Optional<Integer> sum1 = numbers.stream()
    .reduce(Integer::sum);

// 形式2:初始值 + BinaryOperator
Integer sum2 = numbers.stream()
    .reduce(0, Integer::sum);

// 形式3:复杂归约(适用于并行流)
Integer sum3 = numbers.parallelStream()
    .reduce(0, Integer::sum, Integer::sum);

// collect - 收集器(最强大的终端操作)
// 1. 转换为集合
List<String> nameList = users.stream()
    .map(User::getName)
    .collect(Collectors.toList());

Set<String> nameSet = users.stream()
    .map(User::getName)
    .collect(Collectors.toSet());

// 2. 转换为Map
Map<Long, User> userMap = users.stream()
    .collect(Collectors.toMap(User::getId, Function.identity()));

// 处理重复key
Map<String, User> userByName = users.stream()
    .collect(Collectors.toMap(
        User::getName,
        Function.identity(),
        (existing, replacement) -> existing  // 合并函数
    ));

// 3. 分组
Map<String, List<User>> usersByCity = users.stream()
    .collect(Collectors.groupingBy(User::getCity));

// 多级分组
Map<String, Map<String, List<User>>> usersByCityAndGender = users.stream()
    .collect(Collectors.groupingBy(User::getCity,
        Collectors.groupingBy(User::getGender)));

// 4. 分区(分为true/false两组)
Map<Boolean, List<User>> partitioned = users.stream()
    .collect(Collectors.partitioningBy(user -> user.getAge() >= 18));

// 5. 连接字符串
String joinedNames = users.stream()
    .map(User::getName)
    .collect(Collectors.joining(", ", "[", "]"));

// 6. 统计汇总
IntSummaryStatistics stats = users.stream()
    .collect(Collectors.summarizingInt(User::getAge));
// 包含:count, sum, min, max, average

// 7. 自定义收集器
List<User> customList = users.stream()
    .collect(ArrayList::new, List::add, List::addAll);

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】

三、高级操作与模式

1. 无限流处理

java

复制

下载

复制代码
// 生成斐波那契数列
Stream.iterate(new long[]{0, 1}, t -> new long[]{t[1], t[0] + t[1]})
    .limit(20)
    .map(t -> t[0])
    .forEach(System.out::println);

// 生成随机数流
Stream.generate(Math::random)
    .limit(10)
    .forEach(System.out::println);

2. 并行流处理

java

复制

下载

复制代码
// 顺序流 vs 并行流
List<Integer> numbers = IntStream.range(1, 1000000)
    .boxed()
    .collect(Collectors.toList());

// 顺序处理
long start1 = System.currentTimeMillis();
long sum1 = numbers.stream()
    .mapToInt(Integer::intValue)
    .sum();
long time1 = System.currentTimeMillis() - start1;

// 并行处理
long start2 = System.currentTimeMillis();
long sum2 = numbers.parallelStream()
    .mapToInt(Integer::intValue)
    .sum();
long time2 = System.currentTimeMillis() - start2;

3. 流操作链优化

java

复制

下载

复制代码
// 优化前 - 性能较差
List<String> result = users.stream()
    .filter(user -> user.getAge() > 18)
    .map(User::getName)
    .filter(name -> name.startsWith("A"))
    .map(String::toUpperCase)
    .sorted()
    .collect(Collectors.toList());

// 优化后 - 调整操作顺序
List<String> optimized = users.stream()
    .filter(user -> user.getAge() > 18)
    .map(User::getName)
    .filter(name -> name.startsWith("A"))
    .sorted()  // 尽早排序可以减少后续操作的数据量
    .map(String::toUpperCase)
    .collect(Collectors.toList());

四、性能考量与最佳实践

1. 性能对比基准测试

java

复制

下载

复制代码
// 测试不同操作的性能影响
public class StreamPerformance {
    
    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @OutputTimeUnit(TimeUnit.MILLISECONDS)
    public void testForLoop() {
        long sum = 0;
        for (int i = 0; i < 1_000_000; i++) {
            if (i % 2 == 0) {
                sum += i * 2;
            }
        }
    }
    
    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @OutputTimeUnit(TimeUnit.MILLISECONDS)
    public void testStream() {
        long sum = IntStream.range(0, 1_000_000)
            .filter(i -> i % 2 == 0)
            .map(i -> i * 2)
            .sum();
    }
    
    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @OutputTimeUnit(TimeUnit.MILLISECONDS)
    public void testParallelStream() {
        long sum = IntStream.range(0, 1_000_000)
            .parallel()
            .filter(i -> i % 2 == 0)
            .map(i -> i * 2)
            .sum();
    }
}

2. 性能影响因素分析

影响性能的关键因素:
  1. 数据量大小

    • 小数据量(<1000):传统循环可能更快

    • 大数据量:Stream优势明显

  2. 操作类型

    • 低开销操作(map、filter):Stream性能好

    • 高开销操作(sorted、distinct):需要评估

  3. 并行流适用性

    java

    复制

    下载

    复制代码
    // 适合并行的场景:
    // 1. 数据量大(>10000)
    // 2. 计算密集型操作
    // 3. 操作之间无状态依赖
    
    // 不适合并行的场景:
    // 1. 数据量小
    // 2. IO密集型操作
    // 3. 操作顺序敏感
    // 4. 共享可变状态

3. 性能优化策略

🎯 操作顺序优化

java

复制

下载

复制代码
// 原则:尽早过滤,推迟排序
// 坏例子
List<String> bad = list.stream()
    .sorted()                     // 过早排序
    .filter(s -> s.length() > 3)  // 排序了很多不需要的元素
    .map(String::toUpperCase)
    .collect(Collectors.toList());

// 好例子
List<String> good = list.stream()
    .filter(s -> s.length() > 3)  // 先过滤,减少数据量
    .sorted()                     // 只对需要的数据排序
    .map(String::toUpperCase)
    .collect(Collectors.toList());
🔄 避免装箱开销

java

复制

下载

复制代码
// 使用原始类型特化流
// 错误:有装箱开销
List<Integer> list = new ArrayList<>();
int sum = list.stream()
    .mapToInt(Integer::intValue)  // 额外拆箱
    .sum();

// 正确:直接使用IntStream
IntStream intStream = IntStream.range(0, 1000000);
int sum = intStream.sum();  // 无装箱开销

// 性能对比(使用JMH测试):
// IntStream.sum(): 0.234 ms/op
// Stream<Integer> + mapToInt: 0.567 ms/op
// Stream<Integer> + reduce: 0.891 ms/op
📊 并行流调优

java

复制

下载

复制代码
// 自定义ForkJoinPool
ForkJoinPool customPool = new ForkJoinPool(4);  // 设置并行度
long result = customPool.submit(() ->
    list.parallelStream()
        .mapToInt(Integer::intValue)
        .sum()
).get();

// 并行流最佳实践:
// 1. 设置合适的并行度
// 2. 避免共享可变状态
// 3. 使用无状态操作
// 4. 考虑负载均衡

4. 内存使用考量

java

复制

下载

复制代码
// Stream vs 传统集合的内存使用
public class MemoryUsage {
    
    // Stream处理大文件的优势
    public void processLargeFile() throws IOException {
        // 传统方式:读取整个文件到内存
        List<String> lines = Files.readAllLines(Paths.get("large.txt"));
        // 内存占用高
        
        // Stream方式:流式处理
        try (Stream<String> stream = Files.lines(Paths.get("large.txt"))) {
            long count = stream.filter(line -> line.contains("error"))
                               .count();
        }
        // 内存占用低
    }
    
    // 避免中间集合的创建
    public void avoidIntermediateCollections() {
        // 不好:创建了中间集合
        List<String> intermediate = users.stream()
            .map(User::getName)
            .collect(Collectors.toList());  // 中间集合
        
        List<String> result = intermediate.stream()
            .filter(name -> name.startsWith("A"))
            .collect(Collectors.toList());
        
        // 好:直接链式操作
        List<String> optimized = users.stream()
            .map(User::getName)
            .filter(name -> name.startsWith("A"))
            .collect(Collectors.toList());
    }
}

五、常见陷阱与解决方案

1. 流只能消费一次

java

复制

下载

复制代码
// 错误示例
Stream<String> stream = list.stream();
stream.forEach(System.out::println);
stream.forEach(System.out::println);  // IllegalStateException

// 解决方案:每次需要时重新创建流
Supplier<Stream<String>> streamSupplier = () -> list.stream();
streamSupplier.get().forEach(System.out::println);
streamSupplier.get().forEach(System.out::println);  // 正常

2. 并行流的线程安全问题

java

复制

下载

复制代码
// 错误:并行修改共享状态
List<Integer> list = new ArrayList<>();
IntStream.range(0, 10000).parallel()
    .forEach(i -> list.add(i));  // ConcurrentModificationException

// 正确1:使用线程安全集合
List<Integer> safeList = Collections.synchronizedList(new ArrayList<>());
IntStream.range(0, 10000).parallel()
    .forEach(i -> safeList.add(i));

// 正确2:使用collect方法
List<Integer> collected = IntStream.range(0, 10000).parallel()
    .boxed()
    .collect(Collectors.toList());

3. 空指针异常处理

java

复制

下载

复制代码
// 使用Optional避免NPE
Optional<String> result = users.stream()
    .map(User::getAddress)
    .filter(Objects::nonNull)
    .map(Address::getCity)
    .filter(city -> city.startsWith("A"))
    .findFirst();

// 使用flatMap处理嵌套Optional
Optional<String> nested = users.stream()
    .map(User::getAddress)
    .flatMap(Optional::stream)  // Java 9+
    .map(Address::getCity)
    .findFirst();

六、实战应用场景

1. 数据库查询结果处理

java

复制

下载

复制代码
// 模拟从数据库获取的数据
List<Order> orders = orderRepository.findAll();

// 复杂的数据处理
Map<Long, Double> userTotalSpent = orders.stream()
    .filter(order -> order.getStatus() == OrderStatus.COMPLETED)
    .filter(order -> order.getCreateTime().isAfter(LocalDate.now().minusMonths(1)))
    .collect(Collectors.groupingBy(
        Order::getUserId,
        Collectors.summingDouble(Order::getTotalAmount)
    ));

// 找出消费最高的用户
Optional<Map.Entry<Long, Double>> topSpender = userTotalSpent.entrySet().stream()
    .max(Map.Entry.comparingByValue());

2. 批量数据处理

java

复制

下载

复制代码
// 批量更新用户状态
List<User> updatedUsers = users.stream()
    .peek(user -> {
        if (user.isInactive()) {
            user.setStatus(UserStatus.INACTIVE);
        }
    })
    .collect(Collectors.toList());

// 分批处理(每100条一批)
int batchSize = 100;
List<List<User>> batches = IntStream.range(0, users.size())
    .boxed()
    .collect(Collectors.groupingBy(index -> index / batchSize))
    .values().stream()
    .map(indices -> indices.stream()
        .map(users::get)
        .collect(Collectors.toList()))
    .collect(Collectors.toList());

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】

七、性能测试数据参考

操作类型 数据量 传统循环 Stream 并行Stream
简单过滤 100万 15ms 18ms 5ms
复杂映射 100万 45ms 52ms 12ms
排序 10万 25ms 28ms 8ms
分组 10万 32ms 35ms 9ms
归约求和 100万 8ms 10ms 3ms

测试环境:JDK 17, Intel i7-10700, 16GB RAM

八、最佳实践总结

使用Stream的场景:

  1. 数据处理流水线:多个转换操作链式调用

  2. 集合复杂操作:分组、分区、统计等

  3. 并行处理:大数据量计算密集型任务

  4. 函数式风格:代码更声明式,易读易维护

避免使用Stream的场景:

  1. 简单迭代:只有一个简单操作

  2. 性能关键路径:需要极致性能的场景

  3. 异常处理复杂:需要精细的异常控制

  4. 状态依赖操作:操作间有复杂的依赖关系

性能黄金法则:

  1. 数据量决定并行度:小数据用串行,大数据考虑并行

  2. 操作顺序优化:filter在前,sorted在后

  3. 避免装箱拆箱:使用原始类型特化流

  4. 合理使用短路操作:findFirst、limit等

  5. 监控内存使用:避免创建不必要的中间集合

记住 :Stream API的价值不仅在于性能,更在于代码的表达力可维护性。在大多数业务场景下,Stream带来的代码简洁性和可读性提升,远比微小的性能差异更重要。

相关推荐
爱装代码的小瓶子2 小时前
【c++进阶】从C++98到C++11的奇妙旅程(故事科普版)
开发语言·c++
智航GIS2 小时前
2.3 运算符详解
开发语言·python
屋顶那猫2 小时前
使用pyinstaller打包pytest项目
python·pytest
web3.08889992 小时前
接入API-自动化批量获取淘宝商品详情数据
开发语言·python
刹那间的回眸x.y3 小时前
UnitTestReport挺好用
python
世转神风-3 小时前
qt-在字符串中指定位置插入字符串
开发语言·qt
时光呀时光慢慢走3 小时前
C# WinForms 实战:MQTTS 客户端开发(与 STM32 设备通信)
开发语言·c#
Tony11543 小时前
Windows无法安装到这个磁盘。选中的磁盘具有MBR分区表。在EFI系统上,Windows只能安装到GPT磁盘。
windows·diskgenius
码农水水3 小时前
腾讯Java面试被问:阻塞队列BlockingQueue的实现原理
java·后端·python·面试