面试视角下的Java 8+ Stream API与Optional实践

32 阅读3分钟

在面试中,Java 8 的 Stream API 和 Optional 是常被考察的点。面试官喜欢问“如何处理集合数据”“如何避免空指针异常”,甚至会给你一段复杂嵌套对象,让你在短时间写出安全、简洁的代码。这里,我们把两个技术点结合起来,展示如何在真实业务场景中使用,同时解析背后的技术思路。

1️⃣ Stream API 示例解析

Stream API 是函数式风格处理集合的利器,比传统循环更易读、更短、更易组合操作。

技术点filtermapdistinctsortedcollectreduceflatMap

为什么用

  • filter 用于筛选元素,避免手动for循环里的if判断。
  • map 用于字段转换,减少临时变量。
  • distinct 去重,保证唯一值。
  • sorted + limit 常用于Top N场景。
  • collect 收集流操作结果到集合。
  • flatMap 用于嵌套集合扁平化,处理复杂数据结构。
  • reduce / mapToDouble 聚合操作,如求总金额、总收入。

面试考察点

  • 处理集合数据的能力
  • 对流中间操作 vs 终端操作的理解
  • 性能意识(先筛选再映射)

示例代码

import java.util.*;
import java.util.stream.*;

class Order {
    private String id, customer, status, category;
    private double amount;
    public Order(String id, String customer, double amount, String status, String category) {
        this.id = id; this.customer = customer; this.amount = amount;
        this.status = status; this.category = category;
    }
    public String getId() { return id; }
    public String getCustomer() { return customer; }
    public double getAmount() { return amount; }
    public String getStatus() { return status; }
    public String getCategory() { return category; }
    @Override
    public String toString() {
        return String.format("[%s, %s, $%.2f, %s, %s]", id, customer, amount, status, category);
    }
}

public class StreamExample {
    public static void main(String[] args) {
        List<Order> orders = Arrays.asList(
            new Order("A01", "Alice", 1200, "COMPLETED", "ELECTRONICS"),
            new Order("B02", "Bob", 450, "COMPLETED", "CLOTHING"),
            new Order("C03", "Charlie", 800, "PENDING", "ELECTRONICS"),
            new Order("D04", "Alice", 200, "CANCELLED", "FOOD"),
            new Order("E05", "David", 1500, "COMPLETED", "ELECTRONICS")
        );

        // 1. 筛选已完成订单
        System.out.println("Completed Orders:");
        orders.stream()
            .filter(o -> "COMPLETED".equals(o.getStatus()))
            .forEach(System.out::println);

        // 2. 获取所有客户去重
        List<String> customers = orders.stream()
            .map(Order::getCustomer)
            .distinct()
            .collect(Collectors.toList());
        System.out.println("All Customers: " + customers);

        // 3. 高价值电子产品订单
        List<String> highValue = orders.stream()
            .filter(o -> "ELECTRONICS".equals(o.getCategory()))
            .filter(o -> o.getAmount() > 1000)
            .map(Order::getId)
            .collect(Collectors.toList());
        System.out.println("High-Value Electronics: " + highValue);

        // 4. 已完成订单总收入
        double totalRevenue = orders.stream()
            .filter(o -> "COMPLETED".equals(o.getStatus()))
            .mapToDouble(Order::getAmount)
            .sum();
        System.out.println("Total Revenue: $" + totalRevenue);
    }
}

2️⃣ Optional: 避免 NullPointerException

技术点Optional.ofNullablemapfilterorElseorElseGetorElseThrowifPresent

为什么用

  • 避免 NullPointerException,这是面试必考点
  • 链式操作安全地访问深层属性
  • orElseGet 可以延迟计算,提高性能
  • orElseThrow 强制要求值存在,提高API安全性

示例代码

import java.util.*;

class Address { 
    private String city; 
    public Address(String city){this.city=city;}
    public String getCity(){return city;} 
}

class User {
    private String name; private Address address;
    public User(String name, Address address){this.name=name; this.address=address;}
    public Address getAddress(){return address;}
}

class UserService {
    private Map<String, User> users = new HashMap<>();
    public UserService() {
        users.put("u1", new User("Alice", new Address("NYC")));
        users.put("u2", new User("Bob", null));
    }
    public Optional<User> findUser(String id) { return Optional.ofNullable(users.get(id)); }

    // 获取用户城市,防止NPE
    public String getUserCity(String id){
        return findUser(id)
            .map(User::getAddress)
            .map(Address::getCity)
            .orElse("Unknown City");
    }
}

面试考察点

  • 传统 if (user != null && user.getAddress() != null) 会显得啰嗦
  • Optional 链式调用清晰、安全、可读
  • 面试官可能会问“orElse和orElseGet有什么区别”“为什么不用get()直接取值”

3️⃣ 流式 API 常用操作表(面试备考)

操作类型用途示例
filter()中间筛选元素.filter(o -> o.getAmount()>100)
map()中间映射转换.map(Order::getCustomer)
flatMap()中间嵌套流扁平化.flatMap(List::stream)
distinct()中间去重.distinct()
sorted()中间排序.sorted(Comparator.comparing(Order::getAmount))
limit()中间前N元素.limit(5)
skip()中间跳过前N元素.skip(2)
forEach()终端遍历.forEach(System.out::println)
collect()终端收集.collect(Collectors.toList())
reduce()终端聚合.reduce(0, Integer::sum)
count()终端计数.count()
anyMatch()终端是否存在.anyMatch(o -> o.getAmount()>1000)
allMatch()终端是否全部符合.allMatch(o -> o.getStatus().equals("COMPLETED"))
findFirst()终端获取第一个.findFirst()

4️⃣ 技术点总结(面试角度)

  • Stream:处理集合、聚合、分组、排序,展示函数式思维
  • Optional:安全访问对象、避免空指针,展示API设计意识
  • 面试思路:面试官考察的不只是能跑的代码,更重要的是你对安全性、性能、可读性、函数式思维的理解