跳转至

函数式编程 (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表达式最佳实践

  1. 保持简洁:Lambda应该简短,复杂逻辑抽取为方法
  2. 使用方法引用:能用方法引用就不用Lambda
  3. 避免副作用:Lambda内不要修改外部变量
  4. 类型推断:让编译器推断类型,减少代码

7.2 Stream API最佳实践

  1. 优先使用Stream:替代传统的for循环
  2. 避免过长的链式调用:适当换行,提高可读性
  3. 注意性能:并行流不一定更快,需要测试
  4. 避免副作用:中间操作不要修改数据源
  5. 合理使用collect:选择合适的Collector

7.3 Optional最佳实践

  1. 不要用于字段:Optional设计用于返回值
  2. 不要用于参数:直接使用null检查更简单
  3. 避免直接get():使用orElse、orElseGet
  4. 链式调用:利用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值

函数式编程让代码更简洁、更优雅,建议:

  1. 实践为主:多写函数式风格的代码
  2. 理解原理:了解惰性求值、中间操作
  3. 性能考量:不要盲目使用并行流
  4. 可读性优先:代码要清晰易懂

下一篇: 05 - Java新版本特性 →

返回目录: Java 语言基础导航 ←