在面试中,Java 8 的 Stream API 和 Optional 是常被考察的点。面试官喜欢问“如何处理集合数据”“如何避免空指针异常”,甚至会给你一段复杂嵌套对象,让你在短时间写出安全、简洁的代码。这里,我们把两个技术点结合起来,展示如何在真实业务场景中使用,同时解析背后的技术思路。
1️⃣ Stream API 示例解析
Stream API 是函数式风格处理集合的利器,比传统循环更易读、更短、更易组合操作。
技术点:filter、map、distinct、sorted、collect、reduce、flatMap
为什么用:
- 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.ofNullable、map、filter、orElse、orElseGet、orElseThrow、ifPresent
为什么用:
- 避免 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设计意识
- 面试思路:面试官考察的不只是能跑的代码,更重要的是你对安全性、性能、可读性、函数式思维的理解