在Java编程的学习路上,集合框架是基础中的基础,而Java 8引入的Lambda表达式和Stream API则为我们带来了编程范式的革新。本文将从集合迭代器的复习入手,逐步过渡到Lambda表达式的优雅语法,再深入探讨Stream API的强大功能,帮助你理解这一技术演进的脉络与实践技巧。

一、集合迭代器:遍历集合的基石

在Java中,遍历集合是最常见的操作之一,而迭代器(Iterator)正是实现这一操作的核心工具。

1.1 迭代器的核心概念

  • Iterator接口:所有集合遍历的基础接口,包含两个核心方法:

    • boolean hasNext():判断是否还有下一个元素
    • E next():获取下一个元素
  • 列表迭代器ListIterator:专为List集合设计的增强版迭代器,相比普通Iterator新增了更多功能:

特性 普通迭代器Iterator 列表迭代器ListIterator
遍历范围 所有Collection集合 仅限List集合
反向遍历 不支持 支持(hasPrevious()/previous())
元素修改 仅支持删除 支持添加(add())、修改(set())
下标获取 不支持 支持(nextIndex()/previousIndex())

1.2 Iterable与foreach的关系

Iterable接口是所有可迭代集合的父接口,它包含一个iterator()方法用于获取迭代器。而我们常用的foreach循环(增强for循环)本质上就是Iterable接口的语法糖,编译器会自动将其转换为迭代器的操作。

注意:在使用foreach或Iterator遍历集合时,绝对不能直接调用集合的add/remove/sort等方法,否则会抛出ConcurrentModificationException异常。这是因为迭代器内部维护了修改计数器,集合自身的修改会导致计数器不匹配。

二、集合与数组:数据存储的两面

在深入新特性之前,我们先明确集合与数组的核心区别:

特性 数组 集合
元素类型 支持基本类型和对象 仅支持对象(自动装箱拆箱是语法糖)
扩容机制 固定大小,不支持自动扩容 动态扩容,无需手动管理大小
数据结构 仅线性结构 丰富多样(列表、集合、映射、队列等)

三、Lambda表达式:函数式编程的Java实现

Java 8引入的Lambda表达式是一种语法糖,主要用于简化函数式接口的匿名内部类实现,让代码更简洁、更具表达力。

3.1 函数式接口:Lambda的基石

函数式接口(SAM接口 - Single Abstract Method)是指只包含一个抽象方法的接口(可以包含多个静态方法和默认方法)。Java 8中推荐使用@FunctionalInterface注解标记这类接口。

常见的函数式接口:

  • Comparator<T>int compare(T t1, T t2)
  • Predicate<T>boolean test(T t)
  • Consumer<T>void accept(T t)
  • Function<T, R>R apply(T t)

3.2 Lambda表达式的语法

基本语法:

(参数列表) -> { 方法体 }

简化规则

  1. 参数类型可省略(编译器可自动推断)
  2. 单参数时可省略括号()
  3. 方法体只有一条语句时,可省略{};
  4. 若唯一语句是return,需同时省略return
案例1:使用Lambda实现Comparator排序
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

public class LambdaDemo {
    public static void main(String[] args) {
        // 1. 基本的字符串列表操作
        ArrayList<String> fruits = new ArrayList<>();
        Collections.addAll(fruits, "apple", "banana", "cherry", "date", "elderberry");
        
        System.out.println("=== Lambda表达式示例 ===");
        
        // 传统匿名内部类方式
        System.out.println("\n1. 传统匿名内部类排序:");
        Comparator<String> lengthComparator = new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                return Integer.compare(s1.length(), s2.length());
            }
        };
        ArrayList<String> copy1 = new ArrayList<>(fruits);
        copy1.sort(lengthComparator);
        System.out.println("按长度排序: " + copy1);
        
        // Lambda简化版
        System.out.println("\n2. Lambda表达式排序:");
        Comparator<String> lambdaComparator = (s1, s2) -> Integer.compare(s1.length(), s2.length());
        ArrayList<String> copy2 = new ArrayList<>(fruits);
        copy2.sort(lambdaComparator);
        System.out.println("按长度排序: " + copy2);
        
        // 最简洁的使用方式
        System.out.println("\n3. 直接使用Lambda:");
        ArrayList<String> copy3 = new ArrayList<>(fruits);
        copy3.sort((s1, s2) -> Integer.compare(s1.length(), s2.length()));
        System.out.println("按长度排序: " + copy3);
        
        // 2. 其他常见Lambda表达式示例
        System.out.println("\n=== 更多Lambda示例 ===");
        
        // Predicate示例 - 过滤
        System.out.println("\n4. 过滤长度大于5的字符串:");
        List<String> longNames = fruits.stream()
            .filter(s -> s.length() > 5)
            .toList();
        System.out.println("结果: " + longNames);
        
        // Function示例 - 转换
        System.out.println("\n5. 将字符串转换为大写:");
        List<String> upperCase = fruits.stream()
            .map(String::toUpperCase)
            .toList();
        System.out.println("结果: " + upperCase);
        
        // Consumer示例 - 遍历处理
        System.out.println("\n6. 遍历并打印每个元素:");
        fruits.forEach(item -> System.out.println("水果: " + item));
        
        // 3. 方法引用示例
        System.out.println("\n=== 方法引用示例 ===");
        
        // 静态方法引用
        System.out.println("\n7. 使用方法引用排序:");
        ArrayList<String> copy4 = new ArrayList<>(fruits);
        copy4.sort(Comparator.comparing(String::length));
        System.out.println("按长度排序: " + copy4);
        
        // 实例方法引用
        System.out.println("\n8. 按字典序排序:");
        ArrayList<String> copy5 = new ArrayList<>(fruits);
        copy5.sort(String::compareTo);
        System.out.println("字典序: " + copy5);
        
        // 构造方法引用
        System.out.println("\n9. 构造方法引用示例:");
        List<String> stringList = Arrays.asList("hello", "world", "java");
        List<StringBuilder> builders = stringList.stream()
            .map(StringBuilder::new)
            .collect(Collectors.toList());
        System.out.println("StringBuilder列表: " + builders);
        
        System.out.println("\n=== 示例结束 ===");
    }
}

案例2:使用Lambda实现Predicate过滤
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class PredicateDemo {
    public static void main(String[] args) {
        ArrayList<String> fruits = new ArrayList<>();
        Collections.addAll(fruits, "apple", "banana", "cherry", "date", "elderberry", "fig");
        
        System.out.println("=== Predicate接口示例 ===");
        System.out.println("原始列表: " + fruits);
        
        // 1. 传统匿名内部类方式
        System.out.println("\n1. 传统匿名内部类演示:");
        Predicate<String> containsAPredicate = new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.contains("a");
            }
        };
        
        System.out.println("containsAPredicate 测试 'apple': " + containsAPredicate.test("apple")); // true
        System.out.println("containsAPredicate 测试 'cherry': " + containsAPredicate.test("cherry")); // false
        
        Predicate<String> negatedPredicate = containsAPredicate.negate();
        System.out.println("negatedPredicate 测试 'apple': " + negatedPredicate.test("apple")); // false
        System.out.println("negatedPredicate 测试 'cherry': " + negatedPredicate.test("cherry")); // true
        
        ArrayList<String> traditionalFilter = new ArrayList<>(fruits);
        traditionalFilter.removeIf(negatedPredicate); // 移除不包含'a'的
        System.out.println("保留包含'a'的结果: " + traditionalFilter);
        
        // 2. Lambda表达式简化
        System.out.println("\n2. Lambda表达式过滤包含'a'的单词:");
        ArrayList<String> lambdaFilter = new ArrayList<>(fruits);
        lambdaFilter.removeIf(s -> !s.contains("a")); // 保留包含'a'的
        System.out.println("结果: " + lambdaFilter);
        
        // 3. Stream API结合Predicate
        System.out.println("\n3. Stream API过滤:");
        List<String> streamResult = fruits.stream()
            .filter(s -> s.contains("a"))
            .toList();
        System.out.println("包含'a'的单词: " + streamResult);
        
        // 4. 复合Predicate示例
        System.out.println("\n4. 复合条件过滤:");
        Predicate<String> lengthPredicate = s -> s.length() > 4;
        Predicate<String> containsEPredicate = s -> s.contains("e");
        
        // 长度大于4且包含'e'的单词
        List<String> compoundResult = fruits.stream()
            .filter(lengthPredicate.and(containsEPredicate))
            .toList();
        System.out.println("长度>4且包含'e': " + compoundResult);
        
        // 5. 或条件示例
        System.out.println("\n5. 或条件过滤:");
        List<String> orResult = fruits.stream()
            .filter(lengthPredicate.or(containsEPredicate))
            .toList();
        System.out.println("长度>4或包含'e': " + orResult);
        
        // 6. 取反示例
        System.out.println("\n6. 取反条件过滤:");
        List<String> negateResult = fruits.stream()
            .filter(lengthPredicate.negate())
            .toList();
        System.out.println("长度<=4的单词: " + negateResult);
        
        // 7. 自定义复杂Predicate
        System.out.println("\n7. 自定义复杂条件:");
        Predicate<String> customPredicate = s -> {
            // 长度为偶数且不以元音开头
            boolean evenLength = s.length() % 2 == 0;
            boolean notStartWithVowel = !Arrays.asList('a', 'e', 'i', 'o', 'u').contains(Character.toLowerCase(s.charAt(0)));
            return evenLength && notStartWithVowel;
        };
        
        List<String> customResult = fruits.stream()
            .filter(customPredicate)
            .toList();
        System.out.println("自定义条件结果: " + customResult);
        
        System.out.println("\n=== Predicate示例结束 ===");
    }
}

3.3 四大函数式接口家族

Java 8在java.util.function包中提供了丰富的函数式接口,可分为四大类:

  1. 判断型(Predicate):返回boolean,用于条件判断

    // 检查字符串长度是否大于5
    Predicate<String> lengthCheck = s -> s.length() > 5;
    System.out.println(lengthCheck.test("hello")); // false
    
  2. 消费型(Consumer):无返回值,用于处理数据

    // 打印字符串及其长度
    Consumer<String> printInfo = s -> System.out.println(s + " length: " + s.length());
    printInfo.accept("hello"); // hello length: 5
    
  3. 功能型(Function):有输入输出,用于数据转换

    // 将字符串转换为其长度
    Function<String, Integer> stringToLength = s -> s.length();
    System.out.println(stringToLength.apply("hello")); // 5
    
  4. 供给型(Supplier):无输入有输出,用于产生数据

    // 生成随机整数
    Supplier<Integer> randomInt = () -> (int)(Math.random() * 100);
    System.out.println(randomInt.get()); // 随机数
    

四、方法引用:Lambda的进一步简化

方法引用是Lambda表达式的简化形式,当Lambda体只是调用一个已存在的方法时,可以使用方法引用进一步简化代码。

4.1 方法引用的三种形式

  1. 对象::实例方法

    // 打印输出
    Consumer<String> printer = System.out::println;
    printer.accept("Hello World");
    
  2. 类::静态方法

    // 整数比较
    Comparator<Integer> comparator = Integer::compare;
    System.out.println(comparator.compare(3, 5)); // -1
    
  3. 类::实例方法(第一个参数是方法的调用者)

    // 字符串比较
    Comparator<String> strComparator = String::compareTo;
    System.out.println(strComparator.compare("a", "b")); // -1
    
  4. 类::new(构造器引用)

    // 创建字符串数组
    Supplier<String> stringCreator = String::new;
    String s = stringCreator.get(); // 空字符串
    

4.2 方法引用使用条件

使用方法引用需要满足三个条件:

  1. Lambda体中只有一个语句
  2. 该语句是调用某个方法(静态或实例方法)
  3. 方法的参数与Lambda表达式的参数完全一致
案例:方法引用实战
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class MethodReferenceDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "apple", "banana", "cherry", "date");
        
        // 1. 对象::实例方法
        list.forEach(System.out::println);
        
        // 2. 类::静态方法
        list.sort(Integer::compare); // 编译错误!参数类型不匹配
        
        // 正确示例:按长度排序
        list.sort(Comparator.comparingInt(String::length));
        System.out.println(list); // [date, apple, banana, cherry]
        
        // 3. 类::实例方法
        list.sort(String::compareTo);
        System.out.println(list); // [apple, banana, cherry, date]
    }
}

五、Stream API:数据处理的流水线

Stream API是Java 8引入的另一个重大特性,它为集合数据的处理提供了类似SQL查询的声明式操作,让数据处理更简洁、高效。

5.1 Stream的核心特点

  1. 三步操作流程

    • 创建Stream:从数据源获取流
    • 中间操作:对数据进行处理(过滤、映射、排序等)
    • 终结操作:产生结果(收集、统计、遍历等)
  2. 懒加载特性:中间操作不会立即执行,直到遇到终结操作才会一次性处理

  3. 不可变性:Stream操作不会修改源数据,每个操作都会返回新的Stream

  4. 一次性:Stream只能被消费一次,再次使用会抛出异常

5.2 创建Stream的常用方式

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class StreamCreation {
    public static void main(String[] args) {
        // 1. 从集合创建
        List<String> list = new ArrayList<>();
        Stream<String> streamFromList = list.stream();
        
        // 2. 从数组创建
        int[] array = {1, 2, 3, 4, 5};
        IntStream streamFromArray = Arrays.stream(array);
        
        // 3. 直接创建
        Stream<String> streamFromValues = Stream.of("a", "b", "c");
        
        // 4. 生成无限流(需要限制大小)
        Stream<Integer> infiniteStream = Stream.generate(() -> (int)(Math.random() * 100))
                                             .limit(5); // 只取前5个元素
        
        // 5. 迭代生成
        Stream<Integer> iteratedStream = Stream.iterate(0, n -> n + 2)
                                              .limit(5); // 0, 2, 4, 6, 8
    }
}

5.3 中间操作:数据处理的流水线

常用中间操作:

方法 描述
filter(Predicate) 过滤元素
map(Function) 元素转换
sorted()/sorted(Comparator) 排序
distinct() 去重(基于equals())
limit(long) 限制元素数量
skip(long) 跳过前n个元素
peek(Consumer) 调试用,查看流中元素
案例:中间操作链
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class StreamMiddleOperations {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("apple", "banana", "cherry", "date", "apple");
        
        // 处理流程:去重 -> 过滤长度>5 -> 转换为大写 -> 排序
        Stream<String> processedStream = words.stream()
            .distinct()
            .filter(word -> word.length() > 5)
            .map(String::toUpperCase)
            .sorted();
        
        // 终结操作:打印结果
        processedStream.forEach(System.out::println);
        // 输出:APPLE, BANANA, CHERRY
    }
}

5.4 终结操作:获取处理结果

常用终结操作:

方法 描述
forEach(Consumer) 遍历元素
collect(Collector) 收集结果到集合
count() 计数
findFirst()/findAny() 查找元素
allMatch()/anyMatch()/noneMatch() 匹配判断
max(Comparator)/min(Comparator) 最大值/最小值
reduce() 归约计算
案例:综合数据处理
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public class StreamTerminalOperations {
    public static void main(String[] args) {
        List<Student> students = Arrays.asList(
            new Student("Alice", 90),
            new Student("Bob", 85),
            new Student("Charlie", 95),
            new Student("David", 80),
            new Student("Eve", 90)
        );
        
        // 1. 收集成绩大于85的学生姓名
        List<String> highScorers = students.stream()
            .filter(s -> s.getScore() > 85)
            .map(Student::getName)
            .collect(Collectors.toList());
        System.out.println("高分学生: " + highScorers);
        
        // 2. 计算平均成绩
        double averageScore = students.stream()
            .mapToInt(Student::getScore)
            .average()
            .orElse(0);
        System.out.println("平均成绩: " + averageScore);
        
        // 3. 查找最高分学生
        Optional<Student> topStudent = students.stream()
            .max((s1, s2) -> Integer.compare(s1.getScore(), s2.getScore()));
        topStudent.ifPresent(s -> System.out.println("最高分学生: " + s.getName()));
    }
    
    static class Student {
        private String name;
        private int score;
        
        public Student(String name, int score) {
            this.name = name;
            this.score = score;
        }
        
        public String getName() { return name; }
        public int getScore() { return score; }
    }
}

六、总结:从命令式到声明式的转变

Java的发展历程中,从早期的集合迭代到Lambda表达式,再到Stream API,体现了从命令式编程到声明式编程的转变:

  • 命令式编程:关注"怎么做",需要手动控制每一步操作(如使用for循环遍历)
  • 声明式编程:关注"做什么",只需描述想要的结果(如Stream API的链式操作)

Lambda表达式和Stream API不仅让代码更简洁,还提高了可读性和可维护性,同时为并行处理提供了天然支持。掌握这些特性,能让你编写更优雅、高效的Java代码。

建议在实际开发中多使用这些新特性,逐步培养函数式编程思维,这将为你应对复杂数据处理场景提供强大的工具支持。

更多推荐