函数式编程 (Functional Programming)¶
Java 8引入的革命性特性,让代码更简洁、更优雅
目录¶
1. Lambda表达式 (Lambda Expressions)¶
Lambda表达式是一个匿名函数,可以作为参数传递给方法或赋值给变量。
1.1 Lambda语法¶
/**
* Lambda表达式语法
* Lambda Expression Syntax
*
* 基本语法:(参数列表) -> { 方法体 }
*/
public class LambdaSyntaxDemo {
public static void main(String[] args) {
// 1. 无参数Lambda
Runnable task1 = () -> System.out.println("Hello Lambda");
task1.run();
// 2. 单参数Lambda(括号可省略)
Consumer<String> consumer = s -> System.out.println(s);
consumer.accept("Hello");
// 3. 多参数Lambda
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
System.out.println(add.apply(10, 20)); // 30
// 4. 多行方法体(需要大括号和return)
BiFunction<Integer, Integer, Integer> multiply = (a, b) -> {
int result = a * b;
System.out.println("计算结果: " + result);
return result;
};
multiply.apply(5, 6);
// 5. 类型推断(可以省略参数类型)
BiFunction<String, String, Integer> comparator =
(String s1, String s2) -> s1.compareTo(s2);
// 简化为:
BiFunction<String, String, Integer> comparator2 =
(s1, s2) -> s1.compareTo(s2);
}
}
1.2 Lambda vs 匿名内部类¶
/**
* Lambda表达式 vs 匿名内部类
* Lambda vs Anonymous Inner Class
*/
public class LambdaVsAnonymousDemo {
public static void main(String[] args) {
// 传统方式:匿名内部类
Runnable task1 = new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类");
}
};
// Lambda方式:简洁明了
Runnable task2 = () -> System.out.println("Lambda表达式");
// 复杂示例:排序
List<String> list = Arrays.asList("Java", "Python", "C++", "Go");
// 匿名内部类方式
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
});
// Lambda方式
Collections.sort(list, (s1, s2) -> s1.compareTo(s2));
// 更简洁的方法引用
Collections.sort(list, String::compareTo);
}
}
2. 函数式接口 (Functional Interfaces)¶
函数式接口是只有一个抽象方法的接口,可以使用@FunctionalInterface注解标记。
2.1 内置函数式接口¶
Java 8在java.util.function包中提供了丰富的函数式接口:
| 接口 | 方法签名 | 说明 | 示例 |
|---|---|---|---|
| Function |
R apply(T t) |
接受T返回R | 类型转换 |
| Predicate |
boolean test(T t) |
接受T返回boolean | 条件判断 |
| Consumer |
void accept(T t) |
接受T无返回值 | 打印、保存 |
| Supplier |
T get() |
无参数返回T | 工厂方法 |
| UnaryOperator |
T apply(T t) |
接受T返回T | 自增、格式化 |
| BinaryOperator |
T apply(T t1, T t2) |
接受2个T返回T | 求和、比较 |
/**
* 内置函数式接口示例
* Built-in Functional Interfaces Example
*/
public class FunctionalInterfacesDemo {
public static void main(String[] args) {
// 1. Function<T, R> - 转换功能
Function<String, Integer> strLength = s -> s.length();
System.out.println(strLength.apply("Hello")); // 5
Function<Integer, String> intToStr = i -> "数字: " + i;
System.out.println(intToStr.apply(100)); // 数字: 100
// Function组合:andThen 和 compose
Function<Integer, Integer> multiply = x -> x * 2;
Function<Integer, Integer> add = x -> x + 10;
// andThen:先执行multiply,再执行add
Function<Integer, Integer> combined1 = multiply.andThen(add);
System.out.println(combined1.apply(5)); // (5*2)+10=20
// compose:先执行add,再执行multiply
Function<Integer, Integer> combined2 = multiply.compose(add);
System.out.println(combined2.apply(5)); // (5+10)*2=30
// 2. Predicate<T> - 条件判断
Predicate<Integer> isEven = n -> n % 2 == 0;
System.out.println(isEven.test(10)); // true
System.out.println(isEven.test(11)); // false
Predicate<String> isEmpty = s -> s.isEmpty();
Predicate<String> isBlank = s -> s.trim().isEmpty();
// Predicate组合:and, or, negate
Predicate<Integer> greaterThan10 = n -> n > 10;
Predicate<Integer> lessThan100 = n -> n < 100;
Predicate<Integer> range = greaterThan10.and(lessThan100);
System.out.println(range.test(50)); // true
// 3. Consumer<T> - 消费数据
Consumer<String> print = s -> System.out.println("输出: " + s);
print.accept("Hello");
Consumer<String> log = s -> System.out.println("日志: " + s);
// Consumer组合:andThen
Consumer<String> printAndLog = print.andThen(log);
printAndLog.accept("测试");
// 4. Supplier<T> - 提供数据
Supplier<Double> random = () -> Math.random();
System.out.println("随机数: " + random.get());
Supplier<String> dateSupplier = () ->
LocalDateTime.now().toString();
System.out.println("当前时间: " + dateSupplier.get());
// 5. UnaryOperator<T> - 一元操作
UnaryOperator<Integer> square = x -> x * x;
System.out.println(square.apply(5)); // 25
UnaryOperator<String> uppercase = String::toUpperCase;
System.out.println(uppercase.apply("hello")); // HELLO
// 6. BinaryOperator<T> - 二元操作
BinaryOperator<Integer> sum = (a, b) -> a + b;
System.out.println(sum.apply(10, 20)); // 30
BinaryOperator<Integer> max = BinaryOperator.maxBy(Integer::compareTo);
System.out.println(max.apply(10, 20)); // 20
}
}
2.2 自定义函数式接口¶
/**
* 自定义函数式接口
* Custom Functional Interface
*/
@FunctionalInterface
public interface Calculator {
// 唯一的抽象方法
int calculate(int a, int b);
// 可以有默认方法
default int add(int a, int b) {
return a + b;
}
// 可以有静态方法
static int multiply(int a, int b) {
return a * b;
}
}
/**
* 使用自定义函数式接口
*/
public class CustomFunctionalInterfaceDemo {
public static void main(String[] args) {
// 使用Lambda实现
Calculator add = (a, b) -> a + b;
Calculator subtract = (a, b) -> a - b;
Calculator multiply = (a, b) -> a * b;
Calculator divide = (a, b) -> a / b;
System.out.println(add.calculate(10, 5)); // 15
System.out.println(subtract.calculate(10, 5)); // 5
System.out.println(multiply.calculate(10, 5)); // 50
System.out.println(divide.calculate(10, 5)); // 2
// 使用默认方法
System.out.println(add.add(10, 20)); // 30
// 使用静态方法
System.out.println(Calculator.multiply(5, 6)); // 30
}
}
3. 方法引用 (Method References)¶
方法引用是Lambda表达式的简化形式,使用::操作符。
3.1 方法引用的四种类型¶
/**
* 方法引用示例
* Method Reference Example
*/
public class MethodReferenceDemo {
public static void main(String[] args) {
List<String> list = Arrays.asList("Java", "Python", "C++", "Go");
// 1. 静态方法引用:ClassName::staticMethod
// Lambda: s -> Integer.parseInt(s)
Function<String, Integer> parser = Integer::parseInt;
System.out.println(parser.apply("123")); // 123
// 2. 实例方法引用:instance::instanceMethod
String prefix = "Hello, ";
// Lambda: s -> prefix.concat(s)
Function<String, String> greeter = prefix::concat;
System.out.println(greeter.apply("World")); // Hello, World
// 3. 类的实例方法引用:ClassName::instanceMethod
// Lambda: (s1, s2) -> s1.compareTo(s2)
Comparator<String> comparator = String::compareTo;
list.sort(comparator);
// Lambda: s -> s.length()
Function<String, Integer> lengthFunc = String::length;
// Lambda: s -> s.toUpperCase()
list.replaceAll(String::toUpperCase);
System.out.println(list); // [C++, GO, JAVA, PYTHON]
// 4. 构造器引用:ClassName::new
// Lambda: () -> new ArrayList<>()
Supplier<List<String>> listSupplier = ArrayList::new;
List<String> newList = listSupplier.get();
// Lambda: s -> new String(s)
Function<String, String> stringConstructor = String::new;
// 带参数的构造器引用
BiFunction<String, Integer, Person> personConstructor = Person::new;
Person person = personConstructor.apply("张三", 25);
System.out.println(person);
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
3.2 方法引用 vs Lambda¶
// Lambda表达式
list.forEach(s -> System.out.println(s));
// 方法引用
list.forEach(System.out::println);
// Lambda表达式
list.stream().map(s -> s.length()).forEach(System.out::println);
// 方法引用
list.stream().map(String::length).forEach(System.out::println);
4. Stream API¶
Stream是Java 8引入的处理集合的抽象概念,支持函数式操作。
4.1 Stream的创建¶
/**
* Stream创建示例
* Stream Creation Example
*/
public class StreamCreationDemo {
public static void main(String[] args) {
// 1. 从集合创建
List<String> list = Arrays.asList("A", "B", "C");
Stream<String> stream1 = list.stream();
// 2. 从数组创建
String[] array = {"A", "B", "C"};
Stream<String> stream2 = Arrays.stream(array);
// 3. 使用Stream.of()
Stream<String> stream3 = Stream.of("A", "B", "C");
// 4. 无限流
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 2);
infiniteStream.limit(10).forEach(System.out::println); // 0,2,4,6...18
// 5. 生成流
Stream<Double> randomStream = Stream.generate(Math::random);
randomStream.limit(5).forEach(System.out::println);
// 6. 范围流
IntStream.range(1, 5).forEach(System.out::println); // 1,2,3,4
IntStream.rangeClosed(1, 5).forEach(System.out::println); // 1,2,3,4,5
}
}
4.2 Stream的中间操作¶
中间操作返回新的Stream,支持链式调用,采用惰性求值。
/**
* Stream中间操作示例
* Stream Intermediate Operations Example
*/
public class StreamIntermediateDemo {
public static void main(String[] args) {
List<String> list = Arrays.asList(
"Java", "Python", "C++", "Go", "JavaScript", "Ruby"
);
// 1. filter() - 过滤
list.stream()
.filter(s -> s.length() > 4)
.forEach(System.out::println); // Java, Python, JavaScript
// 2. map() - 映射转换
list.stream()
.map(String::length)
.forEach(System.out::println); // 4,6,3,2,10,4
list.stream()
.map(String::toUpperCase)
.forEach(System.out::println); // JAVA, PYTHON...
// 3. flatMap() - 扁平化映射
List<List<String>> nestedList = Arrays.asList(
Arrays.asList("A", "B"),
Arrays.asList("C", "D"),
Arrays.asList("E", "F")
);
nestedList.stream()
.flatMap(List::stream)
.forEach(System.out::println); // A,B,C,D,E,F
// 4. distinct() - 去重
Arrays.asList(1, 2, 2, 3, 3, 3).stream()
.distinct()
.forEach(System.out::println); // 1,2,3
// 5. sorted() - 排序
list.stream()
.sorted()
.forEach(System.out::println); // C++, Go, Java, JavaScript...
list.stream()
.sorted(Comparator.reverseOrder())
.forEach(System.out::println); // 反序
// 6. limit() - 限制数量
list.stream()
.limit(3)
.forEach(System.out::println); // 前3个
// 7. skip() - 跳过元素
list.stream()
.skip(2)
.forEach(System.out::println); // 跳过前2个
// 8. peek() - 查看元素(用于调试)
list.stream()
.peek(s -> System.out.println("处理前: " + s))
.map(String::toUpperCase)
.peek(s -> System.out.println("处理后: " + s))
.collect(Collectors.toList());
}
}
4.3 Stream的终止操作¶
终止操作触发实际计算,返回非Stream结果。
/**
* Stream终止操作示例
* Stream Terminal Operations Example
*/
public class StreamTerminalDemo {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 1. forEach() - 遍历
numbers.stream().forEach(System.out::println);
// 2. collect() - 收集结果
// 转为List
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println(evenNumbers); // [2, 4, 6, 8, 10]
// 转为Set
Set<Integer> set = numbers.stream()
.collect(Collectors.toSet());
// 转为Map
Map<Integer, String> map = numbers.stream()
.collect(Collectors.toMap(
n -> n,
n -> "Number" + n
));
// 分组
Map<Boolean, List<Integer>> partitioned = numbers.stream()
.collect(Collectors.partitioningBy(n -> n % 2 == 0));
System.out.println("偶数: " + partitioned.get(true));
System.out.println("奇数: " + partitioned.get(false));
// 3. reduce() - 归约
// 求和
Optional<Integer> sum = numbers.stream()
.reduce((a, b) -> a + b);
System.out.println("总和: " + sum.get()); // 55
// 带初始值的reduce
Integer sum2 = numbers.stream()
.reduce(0, (a, b) -> a + b);
System.out.println("总和: " + sum2); // 55
// 求最大值
Optional<Integer> max = numbers.stream()
.reduce(Integer::max);
System.out.println("最大值: " + max.get()); // 10
// 4. count() - 计数
long count = numbers.stream()
.filter(n -> n > 5)
.count();
System.out.println("大于5的数量: " + count); // 5
// 5. anyMatch() / allMatch() / noneMatch() - 匹配
boolean hasEven = numbers.stream().anyMatch(n -> n % 2 == 0);
System.out.println("有偶数: " + hasEven); // true
boolean allPositive = numbers.stream().allMatch(n -> n > 0);
System.out.println("全是正数: " + allPositive); // true
boolean noneNegative = numbers.stream().noneMatch(n -> n < 0);
System.out.println("没有负数: " + noneNegative); // true
// 6. findFirst() / findAny() - 查找
Optional<Integer> first = numbers.stream()
.filter(n -> n > 5)
.findFirst();
System.out.println("第一个大于5的数: " + first.get()); // 6
// 7. min() / max() - 最小/最大值
Optional<Integer> min = numbers.stream().min(Integer::compareTo);
Optional<Integer> max2 = numbers.stream().max(Integer::compareTo);
System.out.println("最小值: " + min.get()); // 1
System.out.println("最大值: " + max2.get()); // 10
}
}
4.4 Collectors工具类¶
/**
* Collectors工具类示例
* Collectors Utility Example
*/
public class CollectorsDemo {
public static void main(String[] args) {
List<Person> persons = Arrays.asList(
new Person("张三", 25, "男", 8000),
new Person("李四", 30, "男", 10000),
new Person("王五", 28, "男", 9000),
new Person("赵六", 26, "女", 8500),
new Person("孙七", 32, "女", 11000)
);
// 1. toList() / toSet() / toCollection()
List<String> names = persons.stream()
.map(Person::getName)
.collect(Collectors.toList());
// 2. joining() - 字符串连接
String allNames = persons.stream()
.map(Person::getName)
.collect(Collectors.joining(", "));
System.out.println(allNames); // 张三, 李四, 王五...
String namesWithBrackets = persons.stream()
.map(Person::getName)
.collect(Collectors.joining(", ", "[", "]"));
System.out.println(namesWithBrackets); // [张三, 李四, 王五...]
// 3. groupingBy() - 分组
Map<String, List<Person>> byGender = persons.stream()
.collect(Collectors.groupingBy(Person::getGender));
System.out.println("男性: " + byGender.get("男").size()); // 3
System.out.println("女性: " + byGender.get("女").size()); // 2
// 按年龄段分组
Map<String, List<Person>> byAgeGroup = persons.stream()
.collect(Collectors.groupingBy(p -> {
if (p.getAge() < 30) return "青年";
else return "中年";
}));
// 4. partitioningBy() - 分区(特殊的分组,只分两组)
Map<Boolean, List<Person>> partition = persons.stream()
.collect(Collectors.partitioningBy(p -> p.getSalary() > 9000));
System.out.println("高薪: " + partition.get(true).size());
System.out.println("低薪: " + partition.get(false).size());
// 5. counting() - 计数
Long count = persons.stream()
.collect(Collectors.counting());
// 6. summingInt() / summingDouble() - 求和
Integer totalAge = persons.stream()
.collect(Collectors.summingInt(Person::getAge));
System.out.println("总年龄: " + totalAge);
Double totalSalary = persons.stream()
.collect(Collectors.summingDouble(Person::getSalary));
System.out.println("总工资: " + totalSalary);
// 7. averagingInt() / averagingDouble() - 平均值
Double avgAge = persons.stream()
.collect(Collectors.averagingInt(Person::getAge));
System.out.println("平均年龄: " + avgAge);
// 8. maxBy() / minBy() - 最大/最小值
Optional<Person> oldestPerson = persons.stream()
.collect(Collectors.maxBy(Comparator.comparing(Person::getAge)));
System.out.println("最年长: " + oldestPerson.get().getName());
// 9. summarizingInt() - 统计信息
IntSummaryStatistics stats = persons.stream()
.collect(Collectors.summarizingInt(Person::getAge));
System.out.println("年龄统计: " + stats);
// 输出:IntSummaryStatistics{count=5, sum=141, min=25, average=28.2, max=32}
}
}
5. Optional¶
Optional是Java 8引入的容器类,用于优雅地处理null值,避免NullPointerException。
5.1 创建Optional¶
/**
* Optional创建示例
* Optional Creation Example
*/
public class OptionalCreationDemo {
public static void main(String[] args) {
// 1. Optional.of() - 不能为null
Optional<String> opt1 = Optional.of("Hello");
// Optional<String> opt2 = Optional.of(null); // NullPointerException!
// 2. Optional.ofNullable() - 可以为null
Optional<String> opt3 = Optional.ofNullable("Hello");
Optional<String> opt4 = Optional.ofNullable(null); // 允许null
// 3. Optional.empty() - 空Optional
Optional<String> opt5 = Optional.empty();
// 判断是否有值
System.out.println(opt1.isPresent()); // true
System.out.println(opt5.isPresent()); // false
// Java 11+: isEmpty()
System.out.println(opt5.isEmpty()); // true
}
}
5.2 Optional的使用¶
/**
* Optional使用示例
* Optional Usage Example
*/
public class OptionalUsageDemo {
public static void main(String[] args) {
Optional<String> opt = Optional.of("Hello");
Optional<String> empty = Optional.empty();
// 1. isPresent() + get() - 传统方式(不推荐)
if (opt.isPresent()) {
System.out.println(opt.get());
}
// 2. orElse() - 有值返回值,无值返回默认值
String value1 = opt.orElse("Default");
System.out.println(value1); // Hello
String value2 = empty.orElse("Default");
System.out.println(value2); // Default
// 3. orElseGet() - 通过Supplier提供默认值(惰性求值)
String value3 = empty.orElseGet(() -> "Default from Supplier");
System.out.println(value3);
// orElse vs orElseGet的区别
System.out.println("=== orElse ===");
opt.orElse(getDefaultValue()); // 无论有没有值都会执行
System.out.println("=== orElseGet ===");
opt.orElseGet(() -> getDefaultValue()); // 有值时不执行
// 4. orElseThrow() - 无值时抛出异常
try {
String value4 = empty.orElseThrow(() ->
new IllegalArgumentException("值不能为空"));
} catch (Exception e) {
System.out.println(e.getMessage());
}
// 5. ifPresent() - 有值时执行操作
opt.ifPresent(s -> System.out.println("值是: " + s));
// Java 9+: ifPresentOrElse()
opt.ifPresentOrElse(
s -> System.out.println("有值: " + s),
() -> System.out.println("无值")
);
// 6. filter() - 过滤
Optional<String> filtered = opt.filter(s -> s.length() > 3);
System.out.println(filtered.isPresent()); // true
Optional<String> filtered2 = opt.filter(s -> s.length() > 10);
System.out.println(filtered2.isPresent()); // false
// 7. map() - 转换
Optional<Integer> length = opt.map(String::length);
System.out.println(length.get()); // 5
// 8. flatMap() - 扁平化映射
Optional<Optional<String>> nested = Optional.of(Optional.of("Hello"));
Optional<String> flattened = nested.flatMap(o -> o);
System.out.println(flattened.get()); // Hello
}
private static String getDefaultValue() {
System.out.println("执行getDefaultValue()");
return "Default";
}
}
5.3 Optional最佳实践¶
/**
* Optional最佳实践
* Optional Best Practices
*/
public class OptionalBestPractices {
// ✅ 推荐:方法返回Optional
public Optional<User> findUserById(int id) {
// 查询用户...
User user = null; // 假设未找到
return Optional.ofNullable(user);
}
// ❌ 不推荐:Optional作为参数
// public void method(Optional<String> param) { }
// ✅ 推荐:使用orElse提供默认值
public String getUserName(int id) {
return findUserById(id)
.map(User::getName)
.orElse("Unknown");
}
// ✅ 推荐:链式调用
public void example() {
findUserById(1)
.filter(user -> user.getAge() > 18)
.map(User::getEmail)
.ifPresent(email -> sendEmail(email));
}
// ❌ 避免:直接调用get()
public void badPractice() {
Optional<String> opt = Optional.empty();
// String value = opt.get(); // NoSuchElementException!
}
// ✅ 推荐:使用orElseThrow
public String goodPractice() {
Optional<String> opt = Optional.empty();
return opt.orElseThrow(() ->
new IllegalStateException("值不存在"));
}
private void sendEmail(String email) {
System.out.println("发送邮件到: " + email);
}
}
class User {
private String name;
private int age;
private String email;
public String getName() { return name; }
public int getAge() { return age; }
public String getEmail() { return email; }
}
6. 实战案例 (Practical Examples)¶
案例1:员工数据统计¶
/**
* 案例1:员工数据统计
* Case 1: Employee Data Statistics
*/
public class EmployeeStatisticsDemo {
public static void main(String[] args) {
List<Employee> employees = Arrays.asList(
new Employee("张三", 25, "研发部", 8000),
new Employee("李四", 30, "研发部", 12000),
new Employee("王五", 28, "销售部", 9000),
new Employee("赵六", 26, "研发部", 8500),
new Employee("孙七", 32, "销售部", 11000),
new Employee("周八", 29, "人事部", 7000)
);
// 1. 找出工资最高的员工
Optional<Employee> highestPaid = employees.stream()
.max(Comparator.comparing(Employee::getSalary));
highestPaid.ifPresent(e ->
System.out.println("最高工资: " + e.getName() + " - " + e.getSalary())
);
// 2. 按部门分组统计平均工资
Map<String, Double> avgSalaryByDept = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.averagingDouble(Employee::getSalary)
));
avgSalaryByDept.forEach((dept, avgSalary) ->
System.out.println(dept + "平均工资: " + avgSalary)
);
// 3. 统计各部门人数
Map<String, Long> countByDept = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.counting()
));
System.out.println("各部门人数: " + countByDept);
// 4. 找出研发部工资大于9000的员工姓名
List<String> rdHighPaid = employees.stream()
.filter(e -> "研发部".equals(e.getDepartment()))
.filter(e -> e.getSalary() > 9000)
.map(Employee::getName)
.collect(Collectors.toList());
System.out.println("研发部高薪员工: " + rdHighPaid);
// 5. 计算所有员工的总工资
double totalSalary = employees.stream()
.mapToDouble(Employee::getSalary)
.sum();
System.out.println("总工资: " + totalSalary);
}
}
class Employee {
private String name;
private int age;
private String department;
private double salary;
public Employee(String name, int age, String department, double salary) {
this.name = name;
this.age = age;
this.department = department;
this.salary = salary;
}
public String getName() { return name; }
public int getAge() { return age; }
public String getDepartment() { return department; }
public double getSalary() { return salary; }
}
案例2:文件处理¶
/**
* 案例2:文件处理
* Case 2: File Processing
*/
public class FileProcessingDemo {
public static void main(String[] args) throws IOException {
// 读取文件所有行
List<String> lines = Files.lines(Paths.get("data.txt"))
.collect(Collectors.toList());
// 统计单词频率
Map<String, Long> wordFrequency = Files.lines(Paths.get("data.txt"))
.flatMap(line -> Arrays.stream(line.split("\\s+")))
.map(String::toLowerCase)
.collect(Collectors.groupingBy(
word -> word,
Collectors.counting()
));
// 找出出现最多的10个单词
wordFrequency.entrySet().stream()
.sorted(Map.Entry.<String, Long>comparingByValue().reversed())
.limit(10)
.forEach(entry ->
System.out.println(entry.getKey() + ": " + entry.getValue())
);
}
}
7. 最佳实践 (Best Practices)¶
7.1 Lambda表达式最佳实践¶
- 保持简洁:Lambda应该简短,复杂逻辑抽取为方法
- 使用方法引用:能用方法引用就不用Lambda
- 避免副作用:Lambda内不要修改外部变量
- 类型推断:让编译器推断类型,减少代码
7.2 Stream API最佳实践¶
- 优先使用Stream:替代传统的for循环
- 避免过长的链式调用:适当换行,提高可读性
- 注意性能:并行流不一定更快,需要测试
- 避免副作用:中间操作不要修改数据源
- 合理使用collect:选择合适的Collector
7.3 Optional最佳实践¶
- 不要用于字段:Optional设计用于返回值
- 不要用于参数:直接使用null检查更简单
- 避免直接get():使用orElse、orElseGet
- 链式调用:利用map、flatMap、filter
8. 面试高频问题 (Frequently Asked Interview Questions)¶
Q1: Lambda表达式和匿名内部类的区别?⭐⭐⭐⭐⭐¶
答案:
| 维度 | Lambda表达式 | 匿名内部类 |
|---|---|---|
| 语法 | 简洁 | 冗长 |
| this | 指向外部类 | 指向匿名类自身 |
| 编译 | invokedynamic | 生成.class文件 |
| 性能 | 更好 | 较差 |
| 适用范围 | 只能函数式接口 | 任何接口/抽象类 |
Q2: Stream的惰性求值是什么?⭐⭐⭐⭐¶
答案:
Stream的中间操作不会立即执行,只有遇到终止操作时才会触发实际计算。
Stream<Integer> stream = list.stream()
.filter(n -> {
System.out.println("过滤: " + n);
return n > 5;
})
.map(n -> {
System.out.println("映射: " + n);
return n * 2;
});
// 此时还没有任何输出
stream.collect(Collectors.toList()); // 触发计算,才有输出
Q3: Optional的作用是什么?⭐⭐⭐⭐⭐¶
答案:
Optional是一个容器类,用于表示一个值可能存在也可能不存在,避免NullPointerException。
优点: 1. 明确表示可能为空 2. 提供丰富的API处理null 3. 链式调用,代码更优雅 4. 强制考虑null情况
Q4: 什么是函数式接口?⭐⭐⭐⭐⭐¶
答案:
函数式接口是只有一个抽象方法的接口,可以用@FunctionalInterface标记。
特点: - 只有一个抽象方法 - 可以有默认方法和静态方法 - 可以使用Lambda表达式实现 - Java 8提供了大量内置函数式接口
Q5: Stream的并行流性能一定更好吗?⭐⭐⭐⭐¶
答案:
不一定。并行流适合: 1. 数据量大:至少几千个元素 2. 计算密集:每个元素处理耗时较长 3. 无状态操作:元素间无依赖
不适合: 1. 数据量小 2. I/O密集 3. 有顺序要求 4. 需要同步的操作
Q6: map和flatMap的区别?⭐⭐⭐⭐¶
答案:
- map:一对一映射,
Stream<T> -> Stream<R> - flatMap:一对多映射并扁平化,
Stream<T> -> Stream<R>(T可能是集合)
// map:[[1, 2], [3, 4]] -> [[1, 2], [3, 4]]
list.stream().map(x -> x);
// flatMap:[[1, 2], [3, 4]] -> [1, 2, 3, 4]
list.stream().flatMap(List::stream);
Q7: orElse和orElseGet的区别?⭐⭐⭐⭐¶
答案:
- orElse:无论有没有值都会执行
- orElseGet:只有在没有值时才执行(惰性求值)
Optional<String> opt = Optional.of("Hello");
// orElse:方法会执行
opt.orElse(getDefaultValue());
// orElseGet:方法不会执行
opt.orElseGet(() -> getDefaultValue());
Q8: Stream能重复使用吗?⭐⭐⭐¶
答案:
不能。Stream只能使用一次,使用后就关闭了。
Stream<String> stream = list.stream();
stream.forEach(System.out::println); // 第一次使用
stream.forEach(System.out::println); // IllegalStateException!
总结 (Summary)¶
本章介绍了Java 8引入的函数式编程特性:
- ✅ Lambda表达式:简洁的匿名函数
- ✅ 函数式接口:单抽象方法接口
- ✅ 方法引用:Lambda的简化形式
- ✅ Stream API:函数式数据处理
- ✅ Optional:优雅处理null值
函数式编程让代码更简洁、更优雅,建议:
- 实践为主:多写函数式风格的代码
- 理解原理:了解惰性求值、中间操作
- 性能考量:不要盲目使用并行流
- 可读性优先:代码要清晰易懂
下一篇: 05 - Java新版本特性 →
返回目录: Java 语言基础导航 ←