当前位置: 首页 > news >正文

【Java零基础·第12章】Lambda与Stream API

在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.Collections;
import java.util.Comparator;public class LambdaDemo {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();Collections.addAll(list, "apple", "banana", "cherry", "date");// 传统匿名内部类方式Comparator<String> lengthComparator = new Comparator<String>() {@Overridepublic int compare(String s1, String s2) {return Integer.compare(s1.length(), s2.length());}};// Lambda简化版Comparator<String> lambdaComparator = (s1, s2) -> Integer.compare(s1.length(), s2.length());// 更简洁的使用方式list.sort((s1, s2) -> Integer.compare(s1.length(), s2.length()));System.out.println(list); // [date, apple, banana, cherry]}
}
案例2:使用Lambda实现Predicate过滤
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Predicate;public class PredicateDemo {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();Collections.addAll(list, "apple", "banana", "cherry", "date");// 过滤包含字母'a'的单词// 传统匿名内部类Predicate<String> containsAPredicate = new Predicate<String>() {@Overridepublic boolean test(String s) {return s.contains("a");}};// Lambda简化list.removeIf(s -> s.contains("a"));System.out.println(list); // [cherry]}
}

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代码。

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

http://www.dtcms.com/a/499500.html

相关文章:

  • Qemu-NUC980(八):GPIO Controller
  • 外贸型企业网站建设开源商城源码
  • JS逆向-安全辅助项目Yakit热加载魔术方法模版插件语法JSRpc进阶调用接口联动
  • 使用IOT-Tree接入各种数据转BACnet模拟设备输出
  • 网站搭建说明北京海淀区是几环
  • 基于多模态AI技术的传统行业智能化升级路径研究——以开源AI大模型、AI智能名片与S2B2C商城小程序为例
  • 【C语言进阶】指针进阶_数组指针的使用,数组参数和指针参数
  • PySide6 控件插入日期时间(QDateTime)
  • 网站建设 jsp php垂直网站建设
  • 招商网站大全企业官方网站建设的流程
  • 征程 6 | 工具链如何支持 Matmul/Conv 双 int16 输入量化?
  • 【案例实战】鸿蒙分布式调度:跨设备协同实战
  • 中英文网站设计网站开发投标文件
  • Langgraph译文1:让AI自主决策的代理架构
  • 如何让百度能查到自己衡阳专业的关键词优化终报价
  • 为什么.NET的System.IO.Compression无法解压zlib流
  • 微信小程序:日常零售供应系统
  • 安卓如何查看settings是被哪个进程更新的?相关dumpsys命令剖析
  • 网络营销方式案例分析郑州网站优化推广
  • 下载软件太慢的加速操作
  • 网站改版技术要求git wordpress中文免费主题
  • 从应力到位移:混合模式分层损伤起始点推导
  • Gartner 2025年新兴技术成熟度曲线
  • HCIA DAY2
  • 【Go】--文件上传
  • 基于SpringBoot及PostgreSQL的国家减肥食谱管理项目(中):食谱与菜单配置搭建
  • 网站建设 需要准备材料安徽六安特产有哪些
  • Qt的QT_QPA_EGLFS_INTEGRATION环境变量浅解
  • ppt设计网站有哪些银行管理系统网站建设
  • 矽塔 SA8209 输入耐压36V 8A过流保护阈值 过压过流保护芯片 SOT23