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

Java 8 Lambda表达式详解

一、什么是Lambda表达式

Lambda表达式是Java 8引入的最重要的新特性之一,它允许我们将函数作为方法参数,或者将代码作为数据来处理。本质上,Lambda表达式是一个匿名函数,它没有名称,但有参数列表、函数体和返回类型。

二、Lambda表达式语法

2.1 基本语法

(parameters) -> expression

或者

(parameters) -> { statements; }

2.2 语法组成

  • parameters:参数列表,可以有0个或多个参数,参数类型可以显式声明,也可以根据上下文推断(类型推断)。
  • ->:箭头符号,将参数列表和表达式或语句块分开。
  • expression:表达式,如果只有一条语句,可以省略大括号,并且该表达式的值即为 Lambda 表达式的返回值。
  • { statements; }:语句块,如果有多条语句,必须用大括号括起来,并且如果需要返回值,必须使用 return 语句。

2.3 Lambda表达式示例

// 1. 无参数
() -> System.out.println("Hello Lambda")// 2. 一个参数,可省略括号
s -> System.out.println(s)// 3. 多个参数
(a, b) -> a + b// 4. 带类型声明的参数
(String s1, String s2) -> s1.compareTo(s2)// 5. 带代码块的Lambda
(int a, int b) -> {int sum = a + b;return sum;
}

三、Lambda表达式的优缺点

优点:

  1. 代码简洁:Lambda表达式可以替代匿名内部类,减少模板代码,使代码更清晰。

    // 传统方式
    Collections.sort(list, new Comparator<String>() {@Overridepublic int compare(String s1, String s2) {return s1.compareTo(s2);}
    });// Lambda方式
    Collections.sort(list, (s1, s2) -> s1.compareTo(s2));
    
  2. 函数式编程:引入函数式编程风格,使得处理集合和数据流更加方便,结合Stream API可以写出更声明式的代码。支持将函数作为参数传递,为设计更抽象的代码提供了可能。

    // 函数组合示例
    Function<String, String> toUpper = String::toUpperCase;
    Function<String, String> addExclamation = s -> s + "!";Function<String, String> pipeline = toUpper.andThen(addExclamation);
    System.out.println(pipeline.apply("hello")); // 输出: HELLO!
    
  3. 并行处理能力:与Stream API结合,可以更容易地编写并行代码,提高多核处理器上的性能。

    // 串行处理
    List<String> result = list.stream().filter(s -> s.length() > 3).collect(Collectors.toList());// 并行处理
    List<String> result = list.parallelStream().filter(s -> s.length() > 3).collect(Collectors.toList());
    
  4. 增强API:使Java的API更加强大和灵活,例如集合框架的批量操作。

    // 更灵活的API设计
    public void processData(List<String> data, Predicate<String> filter, Function<String, String> mapper) {data.stream().filter(filter).map(mapper).forEach(System.out::println);
    }// 使用
    processData(data, s -> s.startsWith("A"), String::toUpperCase);
    

缺点:

  1. 调试困难:Lambda表达式的堆栈跟踪可能比传统方法复杂,给调试带来一定困难。

    List<String> result = data.stream().filter(s -> s.length() > 5)           // 调试时难以设置断点.map(s -> s.toUpperCase())             // 在Lambda内部调试复杂.collect(Collectors.toList());
    
  2. 性能开销:虽然Lambda表达式在大多数情况下性能不错,但在某些情况下(如捕获变量的Lambda)可能会引入额外的性能开销,因为需要生成新的类。但是,JVM会进行优化,并且随着JVM的升级,这种开销在减少。

    // Lambda表达式会生成额外的类文件
    IntStream.range(0, 1000).map(i -> i * 2)        // 每个Lambda都会生成一个类.filter(i -> i > 100)   // 增加类加载开销.sum();
    
  3. 可读性:对于不熟悉函数式编程的开发者来说,Lambda表达式可能降低代码的可读性。过度使用或嵌套使用Lambda表达式会使代码难以理解。

    // 复杂的Lambda链可能难以理解
    List<String> result = data.stream().flatMap(s -> Arrays.stream(s.split(","))).collect(Collectors.groupingBy(s -> s.substring(0, 1),Collectors.mapping(s -> s.toUpperCase(),Collectors.filtering(s -> s.length() > 3,Collectors.toList()))));
    
  4. 变量捕获限制:Lambda表达式只能用于函数式接口(只有一个抽象方法的接口)。此外,在Lambda表达式中不能修改外部变量(必须是final或effectively final)。

    public void problematicLambda() {int count = 0;List<String> data = Arrays.asList("a", "b", "c");data.forEach(s -> {// count++;  // 编译错误:被Lambda表达式引用的局部变量必须是final或等效final的System.out.println(s);});
    }
    
  5. 异常处理复杂:Lambda中的异常处理比较麻烦,需要包装受检异常。

    // Lambda中的异常处理比较麻烦
    List<String> files = Arrays.asList("file1.txt", "file2.txt");files.stream().map(filename -> {try {return Files.readString(Path.of(filename));} catch (IOException e) {throw new RuntimeException(e);  // 需要包装受检异常}}).forEach(System.out::println);
    

四、函数式接口

Lambda表达式需要函数式接口的支持。

  • 只有一个抽象方法的接口。
  • 可以使用@FunctionalInterface注解标记。
// 自定义函数式接口
@FunctionalInterface
interface MyFunction {int operate(int a, int b);
}public class LambdaDemo {public static void main(String[] args) {// 使用Lambda表达式实现函数式接口MyFunction addition = (a, b) -> a + b;MyFunction multiplication = (a, b) -> a * b;System.out.println(addition.operate(5, 3)); // 输出: 8System.out.println(multiplication.operate(5, 3)); // 输出: 15}
}

内置的函数式接口:

import java.util.function.*;public class BuiltInFunctionalInterfaces {public static void main(String[] args) {// 1. Predicate<T> - 接受一个参数,返回booleanPredicate<String> isLong = s -> s.length() > 5;System.out.println(isLong.test("Hello")); // false// 2. Function<T,R> - 接受一个参数,返回一个结果Function<String, Integer> stringLength = s -> s.length();System.out.println(stringLength.apply("Java")); // 4// 3. Consumer<T> - 接受一个参数,无返回值Consumer<String> printer = s -> System.out.println(s);printer.accept("Hello Consumer");// 4. Supplier<T> - 无参数,返回一个结果Supplier<Double> randomSupplier = () -> Math.random();System.out.println(randomSupplier.get());// 5. UnaryOperator<T> - 接受一个参数,返回同类型结果UnaryOperator<String> toUpper = s -> s.toUpperCase();System.out.println(toUpper.apply("hello")); // HELLO// 6. BinaryOperator<T> - 接受两个同类型参数,返回同类型结果BinaryOperator<Integer> max = (a, b) -> a > b ? a : b;System.out.println(max.apply(10, 20)); // 20}
}

五、方法引用

方法引用是Lambda表达式的一种简写形式。方法引用的四种形式:

import java.util.*;public class MethodReferenceDemo {public static void main(String[] args) {List<String> names = Arrays.asList("Alice", "Bob", "Charlie");// 1. 静态方法引用: ClassName::staticMethodnames.forEach(MethodReferenceDemo::printStatic);// 2. 实例方法引用: instance::instanceMethodMethodReferenceDemo demo = new MethodReferenceDemo();names.forEach(demo::printInstance);// 3. 特定类型的任意对象的方法引用: ClassName::instanceMethodnames.sort(String::compareToIgnoreCase);// 4. 构造方法引用: ClassName::newList<String> upperNames = names.stream().map(String::new).map(String::toUpperCase).collect(Collectors.toList());}public static void printStatic(String s) {System.out.println("Static: " + s);}public void printInstance(String s) {System.out.println("Instance: " + s);}
}

六、变量捕获

Lambda表达式可以捕获外部变量,但有一些限制:

public class VariableCapture {public static void main(String[] args) {String externalString = "Hello";final int finalNumber = 42;// Lambda可以捕获final或effectively final的变量Runnable r = () -> {System.out.println(externalString);System.out.println(finalNumber);// externalString = "Changed"; // 错误:外部变量必须是final或effectively final};new Thread(r).start();}
}

七、Stream API 常用方法

Stream API 提供了强大的数据处理能力。Stream 不存储数据,而是对源数据(如集合)进行各种计算操作。以下是常用的方法:

  • 中间操作:filter, map, flatMap, distinct, sorted, limit, skip, peek
  • 终端操作:forEach, collect, reduce, toArray, count, match, find
  • 数值流:mapToInt, sum, average, range
  • 并行处理:parallelStream

1. 中间操作

中间操作返回一个新的 Stream,可以链式调用。

1.1 filter() - 过滤

过滤元素,只保留满足条件的元素。

List<String> list = Arrays.asList("apple", "banana", "cherry", "date");// 过滤长度大于5的字符串
List<String> result = list.stream().filter(s -> s.length() > 5).collect(Collectors.toList());
// result: ["banana", "cherry"]// 过滤包含特定字符的字符串
List<String> containsA = list.stream().filter(s -> s.contains("a")).collect(Collectors.toList());
// containsA: ["apple", "banana", "date"]

1.2 map() - 映射转换

将元素映射为另一种类型。

List<String> list = Arrays.asList("apple", "banana", "cherry");// 转换为大写
List<String> upperCase = list.stream().map(String::toUpperCase).collect(Collectors.toList());
// upperCase: ["APPLE", "BANANA", "CHERRY"]// 获取字符串长度
List<Integer> lengths = list.stream().map(String::length).collect(Collectors.toList());
// lengths: [5, 6, 6]// 对象属性映射
List<Person> people = Arrays.asList(new Person("Alice", 25),new Person("Bob", 30)
);
List<String> names = people.stream().map(Person::getName).collect(Collectors.toList());
// names: ["Alice", "Bob"]

1.3 flatMap() - 扁平化映射

将每个元素转换为一个 Stream,然后将所有 Stream 连接成一个 Stream。

List<List<String>> listOfLists = Arrays.asList(Arrays.asList("a", "b"),Arrays.asList("c", "d"),Arrays.asList("e", "f")
);// 将多个列表合并为一个流
List<String> flatList = listOfLists.stream().flatMap(List::stream).collect(Collectors.toList());
// flatList: ["a", "b", "c", "d", "e", "f"]// 拆分字符串并扁平化
List<String> words = Arrays.asList("hello world", "java stream");
List<String> individualWords = words.stream().flatMap(s -> Arrays.stream(s.split(" "))).collect(Collectors.toList());
// individualWords: ["hello", "world", "java", "stream"]

1.4 distinct()

去重,根据元素的 equals 方法判断。

List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3, 4);List<Integer> distinctNumbers = numbers.stream().distinct().collect(Collectors.toList());
// distinctNumbers: [1, 2, 3, 4]// 对象去重(需要重写equals和hashCode)
List<Person> people = Arrays.asList(new Person("Alice", 25),new Person("Alice", 25),new Person("Bob", 30)
);
List<Person> distinctPeople = people.stream().distinct().collect(Collectors.toList());

1.5 sorted() - 排序

排序,无参方法按自然序排序,有参方法按比较器排序。

List<String> list = Arrays.asList("banana", "apple", "cherry");// 自然排序
List<String> naturalSorted = list.stream().sorted().collect(Collectors.toList());
// naturalSorted: ["apple", "banana", "cherry"]// 自定义排序
List<String> lengthSorted = list.stream().sorted((s1, s2) -> Integer.compare(s1.length(), s2.length())).collect(Collectors.toList());
// lengthSorted: ["apple", "banana", "cherry"] (如果长度相同)// 使用方法引用
List<String> reverseSorted = list.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
// reverseSorted: ["cherry", "banana", "apple"]

1.6 limit() 和 skip() - 限制和跳过

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);// 取前5个元素
List<Integer> firstFive = numbers.stream().limit(5).collect(Collectors.toList());
// firstFive: [1, 2, 3, 4, 5]// 跳过前3个元素,取接下来的5个
List<Integer> skipThreeTakeFive = numbers.stream().skip(3).limit(5).collect(Collectors.toList());
// skipThreeTakeFive: [4, 5, 6, 7, 8]// 分页实现
int pageSize = 3;
int pageNumber = 1; // 第二页(从0开始)
List<Integer> page = numbers.stream().skip(pageNumber * pageSize).limit(pageSize).collect(Collectors.toList());
// page: [4, 5, 6]

1.7 peek() - 查看元素(调试用)

对每个元素执行一个操作,主要用于调试。

List<String> list = Arrays.asList("apple", "banana", "cherry");List<String> result = list.stream().peek(s -> System.out.println("Before filter: " + s)).filter(s -> s.length() > 5).peek(s -> System.out.println("After filter: " + s)).collect(Collectors.toList());
// 输出:
// Before filter: apple
// Before filter: banana
// After filter: banana
// Before filter: cherry
// After filter: cherry

2. 终端操作

终端操作会触发 Stream 的处理,并产生一个结果或副作用。

2.1 forEach() - 遍历

对每个元素执行一个操作。

List<String> list = Arrays.asList("apple", "banana", "cherry");// 遍历输出
list.stream().forEach(System.out::println);// 执行操作
list.stream().forEach(s -> {String upper = s.toUpperCase();System.out.println(upper);
});

2.2 collect() - 收集结果

将 Stream 中的元素收集到容器中,常用的 Collectors 方法有 toList, toSet, toMap 等。

List<String> list = Arrays.asList("apple", "banana", "cherry", "apple");// 转换为List
List<String> resultList = list.stream().filter(s -> s.length() > 5).collect(Collectors.toList());// 转换为Set(自动去重)
Set<String> resultSet = list.stream().collect(Collectors.toSet());// 转换为Map
Map<String, Integer> lengthMap = list.stream().distinct().collect(Collectors.toMap(s -> s,                    // keyString::length             // value));
// lengthMap: {apple=5, banana=6, cherry=6}// 分组
Map<Integer, List<String>> groupByLength = list.stream().collect(Collectors.groupingBy(String::length));
// groupByLength: {5=[apple], 6=[banana, cherry]}// 分区
Map<Boolean, List<String>> partitionByLength = list.stream().collect(Collectors.partitioningBy(s -> s.length() > 5));
// partitionByLength: {false=[apple], true=[banana, cherry]}// 连接字符串
String joined = list.stream().collect(Collectors.joining(", ", "[", "]"));
// joined: "[apple, banana, cherry, apple]"

2.3 reduce() - 归约操作

将 Stream 中的元素组合起来,得到一个结果。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);// 求和
Optional<Integer> sum = numbers.stream().reduce((a, b) -> a + b);
// sum: 15// 求和(带初始值)
Integer sumWithIdentity = numbers.stream().reduce(0, Integer::sum);
// sumWithIdentity: 15// 求最大值
Optional<Integer> max = numbers.stream().reduce(Integer::max);
// max: 5// 字符串连接
List<String> words = Arrays.asList("Hello", "World", "!");
String sentence = words.stream().reduce("", (a, b) -> a + " " + b).trim();
// sentence: "Hello World !"

2.4 count() - 计数

计算元素个数。

List<String> list = Arrays.asList("apple", "banana", "cherry");long count = list.stream().count();
// count: 3long countLongWords = list.stream().filter(s -> s.length() > 5).count();
// countLongWords: 2

2.5 anyMatch() / allMatch() / noneMatch() - 匹配检查

List<String> list = Arrays.asList("apple", "banana", "cherry");// 是否有任意元素匹配条件
boolean anyMatch = list.stream().anyMatch(s -> s.startsWith("a"));
// anyMatch: true// 是否所有元素都匹配条件
boolean allMatch = list.stream().allMatch(s -> s.length() >= 5);
// allMatch: true// 是否没有元素匹配条件
boolean noneMatch = list.stream().noneMatch(s -> s.contains("z"));
// noneMatch: true

2.6 findFirst() / findAny() - 查找元素

List<String> list = Arrays.asList("apple", "banana", "cherry");// 查找第一个元素
Optional<String> first = list.stream().filter(s -> s.startsWith("b")).findFirst();
// first: "banana"// 查找任意元素(在并行流中更高效)
Optional<String> any = list.stream().parallel().filter(s -> s.length() > 5).findAny();
// any: 可能是 "banana" 或 "cherry"

3. 数值流特化方法

对于数值类型的流,有专门的优化方法。

List<String> stringNumbers = Arrays.asList("1", "2", "3", "4", "5");// 转换为IntStream
IntStream intStream = stringNumbers.stream().mapToInt(Integer::parseInt);// 求和
int sum = stringNumbers.stream().mapToInt(Integer::parseInt).sum();
// sum: 15// 求平均值
double average = stringNumbers.stream().mapToInt(Integer::parseInt).average().orElse(0.0);
// average: 3.0// 最大值
OptionalInt max = stringNumbers.stream().mapToInt(Integer::parseInt).max();
// max: 5// 范围生成
IntStream range = IntStream.range(1, 6); // 1,2,3,4,5
IntStream rangeClosed = IntStream.rangeClosed(1, 5); // 1,2,3,4,5

4. 并行流处理

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);// 顺序流
long sequentialTime = System.currentTimeMillis();
int sequentialSum = numbers.stream().mapToInt(Integer::intValue).sum();
sequentialTime = System.currentTimeMillis() - sequentialTime;// 并行流
long parallelTime = System.currentTimeMillis();
int parallelSum = numbers.parallelStream().mapToInt(Integer::intValue).sum();
parallelTime = System.currentTimeMillis() - parallelTime;System.out.println("Sequential time: " + sequentialTime + "ms");
System.out.println("Parallel time: " + parallelTime + "ms");

八、Lambda表达式在实际中的应用

8.1 集合操作

import java.util.*;
import java.util.stream.*;public class CollectionWithLambda {public static void main(String[] args) {List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");// 使用Lambda进行遍历names.forEach(name -> System.out.println(name));// 使用方法引用names.forEach(System.out::println);// 使用Stream API和Lambda进行过滤和映射List<String> filteredNames = names.stream().filter(name -> name.length() > 4).map(String::toUpperCase).collect(Collectors.toList());System.out.println(filteredNames); // [ALICE, CHARLIE, DAVID]}
}

8.2 线程创建

public class ThreadWithLambda {public static void main(String[] args) {// 传统方式new Thread(new Runnable() {@Overridepublic void run() {System.out.println("传统方式创建的线程");}}).start();// 使用Lambda表达式new Thread(() -> System.out.println("Lambda方式创建的线程")).start();}
}

8.3 排序操作

import java.util.*;public class SortWithLambda {public static void main(String[] args) {List<Person> people = Arrays.asList(new Person("Alice", 25),new Person("Bob", 30),new Person("Charlie", 20));// 使用Lambda表达式按年龄排序Collections.sort(people, (p1, p2) -> p1.getAge() - p2.getAge());// 使用方法引用Collections.sort(people, Comparator.comparing(Person::getAge));people.forEach(System.out::println);}
}class Person {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}// getters and toStringpublic String getName() { return name; }public int getAge() { return age; }@Overridepublic String toString() {return name + "(" + age + ")";}
}

8.4 使用建议

  • 应该使用Lambda的场景

    // 1. 简单的集合操作
    list.stream().filter(s -> s != null).forEach(System.out::println);// 2. 事件处理
    button.addActionListener(e -> handleButtonClick());// 3. 简单的线程任务
    new Thread(() -> doBackgroundWork()).start();// 4. 函数式接口实现
    Comparator<String> comparator = (s1, s2) -> s1.compareTo(s2);
    
  • 应该避免使用Lambda的场景

    // 1. 复杂的业务逻辑
    // 不好的做法:
    list.stream().map(item -> {// 几十行复杂逻辑if (condition1) {// 复杂处理...} else if (condition2) {// 更复杂处理...}return result;
    });// 好的做法:提取方法
    list.stream().map(this::processComplexBusinessLogic);// 2. 需要多次重用的逻辑
    // 不好的做法:重复编写相同的Lambda
    list1.stream().filter(s -> s.length() > 5 && s.contains("abc"));
    list2.stream().filter(s -> s.length() > 5 && s.contains("abc"));// 好的做法:定义Predicate
    Predicate<String> complexFilter = s -> s.length() > 5 && s.contains("abc");
    list1.stream().filter(complexFilter);
    list2.stream().filter(complexFilter);
    

8.5 最佳实践

1. 保持Lambda简洁

// 不好:复杂的Lambda
list.stream().map(s -> {String trimmed = s.trim();if (trimmed.isEmpty()) return "DEFAULT";return trimmed.toUpperCase();
});// 好:使用方法引用和简单Lambda
list.stream().map(String::trim).map(s -> s.isEmpty() ? "DEFAULT" : s).map(String::toUpperCase);

2. 使用方法引用

// 使用Lambda
list.stream().map(s -> s.toUpperCase());// 使用方法引用(更好)
list.stream().map(String::toUpperCase);

3. 限制Lambda长度

// 如果Lambda超过3行,考虑提取方法
list.stream().map(item -> {// 如果逻辑复杂,提取到方法中return processItem(item);
});private ResultType processItem(ItemType item) {// 复杂逻辑放在这里
}
http://www.dtcms.com/a/614709.html

相关文章:

  • vip视频解析网站怎么做离石古楼角网站建设
  • DVL数据协议深度解析:PD0、PD4、PD6格式详解与实践应用
  • Web自动化测试详细流程和步骤
  • P1909 [NOIP 2016 普及组] 买铅笔
  • 萍乡网站开发公司k8s wordpress mysql
  • C++条件判断与循环(二)(算法竞赛)
  • 浏阳建设局网站广告电商怎么做
  • 微信朋友圈做网站推广赚钱吗网站建设费专票会计分录
  • 友元的作用与边界
  • 如何提高英语口语?
  • (6)框架搭建:Qt实战项目之主窗体快捷工具条
  • 做阿里云网站空间建设工程施工合同实例
  • web中间件——Tomcat
  • Linux中管理员和一般用户的用法小结
  • html手机网站模板html5网页设计教程
  • 【Mac】开发环境使用/维护
  • 网站代码设计惠州网站建设排名
  • 精美网站建设wordpress gae
  • 【STM32MP157 异核通信框架学习篇】(10)Linux下Remoteproc相关API (下)
  • 企业建站服务退役军人215专业品牌网站建设
  • 杭州模板网站建站做国外夏令营的网站
  • 基于SpringBoot的房屋租赁管理系统【协同过滤推荐算法+可视化统计+合同签署】
  • 【MySQL | 基础】函数
  • Java Set
  • (60页PPT)数据治理与数据安全防护方案(附下载方式)
  • DSAC-T算法实现控制倒立摆
  • 学校网站建设需要多少钱wordpress添加首页导航
  • 开发区网站制作公司wordpress+系统安装
  • 什么是性能测试?它的分类?(负载测试、压力测试、并发测试等)
  • 4.3 Go 协程:goroutine