场景化与实践编码问题
你有一个 Product 对象列表,每个对象都有 price 和 category 字段。请用 Java Streams 编写 Java 代码,找出“Electronics”类别中产品的平均价格。
回答:
products.stream()
.filter(p -> "Electronics".equals(p.getCategory()))
.mapToDouble(Product::getPrice)
.average()
.orElse(0.0);
这会过滤出“Electronics”产品,将其价格映射到 double 流,计算平均值,并在没有找到产品时提供一个默认值。
描述一个在多线程应用程序中使用 ConcurrentHashMap 而不是 HashMap 的场景。它解决了什么问题?
回答:
当多个线程需要并发地读取和写入一个 Map 时,你会使用 ConcurrentHashMap。HashMap 不是线程安全的,可能导致无限循环或数据损坏。ConcurrentHashMap 提供了线程安全的并发操作,而无需锁定整个 Map,其性能优于 Collections.synchronizedMap()。
你需要逐行处理一个大文件,而无需将整个文件加载到内存中。你如何在 Java 中实现这一点?
回答:
你会使用 BufferedReader 来逐行读取文件。BufferedReader 从输入流中读取字符,并对它们进行缓冲,以实现高效的字符、数组和行的读取。它的 readLine() 方法允许单独处理每一行,从而避免大文件出现内存溢出错误。
解释 Java 集合中“快速失败”(fail-fast)迭代器的概念。提供一个使用它的集合示例。
回答:
快速失败迭代器会在迭代过程中,如果集合被结构性地修改(例如,添加或删除了元素),除了通过迭代器自身的 remove() 方法外,会立即抛出 ConcurrentModificationException。这有助于及早发现并发修改相关的错误。ArrayList 和 HashMap 的迭代器都是快速失败迭代器的例子。
你有一个执行耗时数据库操作的方法。你如何使用 Java 的 CompletableFuture 使该方法变为异步?
回答:
CompletableFuture.supplyAsync(() -> {
// Simulate time-consuming DB operation
try { Thread.sleep(1000); } catch (InterruptedException e) {}
return "DB Result";
});
CompletableFuture.supplyAsync() 在 ForkJoinPool.commonPool() 的一个单独线程中运行提供的 Supplier,并返回一个最终会持有结果的 CompletableFuture。这允许主线程继续执行而不阻塞。
设计一个简单的 User 类,包含 id、username 和 email 字段。确保 id 是唯一的且创建后不可变,并且 username 不能为 null 或空。使用合适的 Java 特性。
回答:
public class User {
private final String id; // Immutable
private String username;
private String email;
public User(String id, String username, String email) {
if (id == null || username == null || username.trim().isEmpty()) {
throw new IllegalArgumentException("ID and username cannot be null/empty.");
}
this.id = id;
this.username = username;
this.email = email;
}
// Getters and Setters for username, email
public String getId() { return id; }
}
为 id 使用 final 可确保其不可变性。构造函数验证处理 id 和 username 的 null/空约束。
你需要实现一个缓存机制来缓存频繁访问的数据。哪个 Java 集合最适合简单的最近最少使用(LRU)缓存,为什么?
回答:
LinkedHashMap 是实现简单 LRU 缓存的理想选择。当使用 accessOrder=true 构建时,它会维护插入顺序或访问顺序。通过重写其 removeEldestEntry() 方法,可以在缓存大小超过预定限制时自动移除最近最少访问的条目,从而高效地实现 LRU 策略。
在访问嵌套属性时(例如 user.getAddress().getStreet()),你将如何优雅地处理潜在的 NullPointerException?
回答:
使用 Optional 是最现代且优雅的方式。你可以链式调用 Optional.ofNullable() 和 map():Optional.ofNullable(user).map(User::getAddress).map(Address::getStreet).orElse("N/A")。这避免了显式的 null 检查,并在链中的任何部分为 null 时提供一个默认值。
你有一个字符串列表,需要计算每个字符串的出现频率。请用 Java 代码使用 Streams 来实现。
回答:
List<String> words = Arrays.asList("apple", "banana", "apple", "orange", "banana");
Map<String, Long> wordCounts = words.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
// Result: {banana=2, orange=1, apple=2}
这使用了 groupingBy 按自身对元素进行分组,并使用 counting 来计算每个组内的出现次数,从而生成一个 Map<String, Long>。
描述一个你会使用 ThreadLocal 变量的场景。它解决了什么问题?
回答:
当需要存储每个线程独有的数据时,会使用 ThreadLocal。例如,在 Web 应用程序中为每个请求管理数据库连接或用户会话上下文。它通过提供变量的线程特定副本,解决了通过方法参数显式传递数据或使用需要复杂同步的共享可变状态的问题。