1. 偏好使用 Lambda 表达式代替匿名类
Lambda 表达式提供了简洁且功能强大的方式来表示行为。它比匿名类更加简洁,并能有效减少冗余代码。
总结: Lambda 表达式使得代码更加简洁、清晰,并且提升了可维护性。
代码示例:
// 错误做法:使用匿名类
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Collections.sort(names, new Comparator<String>() {
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
});
// 正确做法:使用 Lambda 表达式
Collections.sort(names, (s1, s2) -> s1.length() - s2.length());
核心点:Lambda 表达式简化了代码,减少了不必要的类定义。
2. 偏好使用方法引用代替 Lambda 表达式
当 Lambda 表达式只是调用现有方法时,优先使用方法引用。方法引用具有更好的可读性。
总结: 方法引用使代码更简洁,且更加可读,适用于直接引用现有方法的场景。
代码示例:
// 错误做法:使用 Lambda 表达式
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println(name));
// 正确做法:使用方法引用
names.forEach(System.out::println);
核心点:方法引用更简洁且易于理解。
3. 偏好使用标准的函数式接口
Java 8 提供了许多标准的函数式接口,如 Predicate
, Function
, Consumer
等。优先使用这些标准接口,而不是自定义接口。
总结: 使用标准的函数式接口能够提高代码的一致性,减少不必要的自定义。
代码示例:
// 错误做法:自定义函数式接口
@FunctionalInterface
interface MyPredicate {
boolean test(String s);
}
// 正确做法:使用标准的 Predicate 接口
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.removeIf(name -> name.length() > 4);
核心点:使用标准函数式接口减少了不必要的自定义,且与其他 Java 8 特性更加兼容。
4. 明智地使用 Streams
Stream API 是处理集合的强大工具,但它并不总是适用于所有场景。对于简单的操作,传统的迭代方式可能更为高效。
总结: 在性能敏感的场合,应谨慎使用 Stream,因为其引入的开销可能不适合简单操作。
代码示例:
// 错误做法:过度使用 Stream 进行简单迭代
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream().filter(name -> name.length() > 3).forEach(System.out::println);
// 正确做法:直接使用 forEach 循环
for (String name : names) {
if (name.length() > 3) {
System.out.println(name);
}
}
核心点:对于简单的集合操作,传统迭代方式可能更加高效。
5. 偏好使用无副作用的函数
在使用 Stream 时,尽量避免使用会产生副作用的操作。无副作用的函数可以使程序更加稳定、可预测。
总结: 无副作用的函数更易于理解和调试,避免了状态共享问题。
代码示例:
// 错误做法:带有副作用
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream().forEach(name -> {
System.out.println(name);
// 这里修改了外部变量
count++;
});
// 正确做法:避免副作用
names.stream().forEach(System.out::println);
核心点:避免在 Stream 中引入副作用,可以确保代码更加简洁和易于维护。
6. 偏好使用 Collection 而非 Stream 作为返回类型
虽然 Stream 是一种强大的数据处理工具,但它并不适用于所有的场景。对于返回多次使用的数据,使用 Collection 类型作为返回值更加合适。
总结: 在需要多次访问数据时,返回 Collection 比 Stream 更加合适,Stream 更适合于一次性操作。
代码示例:
// 错误做法:返回 Stream
public Stream<String> getNames() {
return names.stream().filter(name -> name.length() > 3);
}
// 正确做法:返回 Collection
public List<String> getNames() {
return names.stream().filter(name -> name.length() > 3).collect(Collectors.toList());
}
核心点:返回 Collection 类型能使数据的使用更加灵活和高效。
7. 使用并行流时要小心
虽然并行流可以显著提高性能,但它适用于数据量较大且操作无副作用的场景。在并行处理时,应谨慎使用,以避免不必要的复杂性和开销。
总结: 并行流提高性能的前提是适当使用,过度使用可能会导致性能下降和调试困难。
代码示例:
// 错误做法:在简单场景下使用并行流
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.parallelStream().forEach(System.out::println);
// 正确做法:使用普通流
names.stream().forEach(System.out::println);
核心点:并行流适用于数据量大且操作无副作用的情况,简单场景下使用普通流更高效。
8. 避免在流中使用共享可变状态
Stream 的并行特性要求所有流操作都必须无副作用。如果流操作使用了共享可变状态,可能导致线程安全问题,因此需要避免。
总结: 在流操作中避免使用共享可变状态可以保证程序的线程安全性。
代码示例:
// 错误做法:在流中使用共享可变状态
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
int[] count = {0}; // 共享可变状态
names.stream().forEach(name -> {
if (name.length() > 3) {
count[0]++;
}
});
System.out.println(count[0]);
// 正确做法:避免共享可变状态
final int[] count = {0};
names.stream().forEach(name -> {
if (name.length() > 3) {
count[0]++;
}
});
System.out.println(count[0]);
核心点:共享可变状态可能导致线程安全问题,应尽量避免。