【Java】lambda表达式,Stream API,函数式编程接口

简介: 【Java】lambda表达式,Stream API,函数式编程接口


1. 引言

在Java中,函数式编程的概念得以实现和推广主要是从Java 8开始,引入了Lambda表达式和函数式接口的支持。这使得Java编程语言在函数式编程方面有了重大改进和增强。

函数式编程在Java中的应用体现在以下几个方面:

  1. Lambda表达式
  2. 函数式接口
  3. Stream API
  4. 方法引用:方法引用是一种简化Lambda表达式的语法形式,用于直接引用已经存在的方法作为Lambda表达式的实现。它可以提高代码的可读性和简洁性,减少冗余的Lambda表达式。
  5. 并行处理:函数式编程强调无副作用和纯函数的特性,这使得函数式编程在并行处理方面更容易实现。Java中的Stream API提供了并行处理数据的能力,利用多核处理器提高数据处理的效率。

函数式编程在Java中的应用可以提供更简洁、灵活和可读性强的代码。它强调将代码分解为小的、可组合的函数,提高了代码的可维护性和可测试性。同时,函数式编程还可以利用并行处理提高程序的性能。

  • 本文将讨论以下三点:Lambda表达式、Stream API和函数式编程接口。

2. Lambda表达式

是什么

Lambda表达式是一种简洁的语法形式,用于表示匿名函数或代码块。它允许我们以更紧凑的方式定义函数或代码块,并将其作为参数传递给其他方法或函数。Lambda表达式的引入是为了支持函数式编程的思想和编程范式。

组成

Lambda表达式的组成部分包括:

参数列表(parameters):用于传递输入参数给Lambda表达式。可以为空,也可以包含一个或多个参数。参数类型可以显式声明,也可以由编译器进行类型推断。

箭头符号(->):用于分隔参数列表和Lambda表达式的主体。

Lambda主体(expression或statements):表示Lambda表达式要执行的代码块。如果主体只有一条语句,可以使用表达式的形式,它的结果将作为Lambda表达式的返回值。如果主体有多条语句,需要使用代码块的形式,使用花括号括起来,并且需要显式使用return语句来返回结果。

示例

Lambda表达式的应用场景包括:

  • 作为函数式接口的实现:Lambda表达式常用于函数式接口的实现,函数式接口是指只包含一个抽象方法的接口。Lambda表达式可以简化匿名内部类的写法,使代码更加简洁,提高可读性。
  • 作为方法参数传递:Lambda表达式可以作为方法的参数传递,这样可以在调用方法时直接定义代码逻辑,而不需要显式地编写具体的实现类或匿名内部类。
  • Stream API的操作:Java 8引入的Stream API中使用Lambda表达式来定义各种集合操作,如筛选、映射、排序、聚合等,使代码更简洁和易读。
// 示例1:无参数的Lambda表达式
Runnable runnable = () -> System.out.println("Hello, Lambda!");
// 示例2:带参数的Lambda表达式
Consumer<String> printer = message -> System.out.println(message);
// 示例3:带多条语句的Lambda表达式
Comparator<Integer> comparator = (a, b) -> {
    int diff = a - b;
    return diff < 0 ? -1 : (diff > 0 ? 1 : 0);
};
// 示例4:Lambda表达式作为方法参数
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.forEach(number -> System.out.println(number));
// 示例5:Stream API的使用
List<Integer> evenNumbers = numbers.stream()
                                   .filter(number -> number % 2 == 0)
                                   .collect(Collectors.toList());

3. Stream API

是什么

Stream API是Java 8引入的用于集合数据处理的功能强大的API。它提供了一种流式处理集合数据的方式,可以进行转换、筛选、映射、聚合等操作,以及支持并行处理。

组成

  • 流(Stream):流是Stream API的核心概念,表示一系列元素的序列。它可以通过集合、数组、I/O通道等数据源创建,并且可以进行各种操作。
  • 中间操作(Intermediate Operations):中间操作是对流进行转换和处理的操作。它们接受一个流作为输入,返回一个新的流作为输出,通常是一个连续的操作链。常见的中间操作包括过滤(filter)、映射(map)、排序(sorted)、去重(distinct)等。
  • 终端操作(Terminal Operations):终端操作是对流进行最终计算或输出的操作。它们接受一个流作为输入,产生一个最终的结果或副作用。常见的终端操作包括收集(collect)、聚合(reduce)、迭代(forEach)等。
  • 并行处理(Parallel Processing):Stream API支持并行处理,可以通过并行流(Parallel Stream)来充分利用多核处理器的优势,提高数据处理的效率。

示例

使用Stream API的基本流程如下:

创建流:可以通过集合、数组、I/O通道等数据源创建流。

中间操作:通过一系列的中间操作对流进行转换和处理,可以进行过滤、映射、排序等操作。每个中间操作都返回一个新的流,可以进行链式调用。

终端操作:对最终的流进行终端操作,产生一个结果或执行一些副作用。终端操作会触发流的计算,产生最终的结果。

当使用Stream API时,我们可以利用其丰富的中间操作和终端操作来处理集合数据。以下是一些常见的Stream API的使用实例:

  1. 过滤操作(filter):
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
List<String> filteredNames = names.stream()
                                 .filter(name -> name.startsWith("A"))
                                 .collect(Collectors.toList());
// 结果:["Alice"]
  1. 映射操作(map):
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> squaredNumbers = numbers.stream()
                                      .map(number -> number * number)
                                      .collect(Collectors.toList());
// 结果:[1, 4, 9, 16, 25]
  1. 排序操作(sorted):
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
List<String> sortedNames = names.stream()
                               .sorted()
                               .collect(Collectors.toList());
// 结果:["Alice", "Bob", "Charlie", "David"]
  1. 去重操作(distinct):
List<Integer> numbers = Arrays.asList(1, 2, 3, 2, 4, 3, 5);
List<Integer> distinctNumbers = numbers.stream()
                                       .distinct()
                                       .collect(Collectors.toList());
// 结果:[1, 2, 3, 4, 5]
  1. 聚合操作(reduce):
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
                 .reduce(0, (a, b) -> a + b);
// 结果:15
  1. 统计操作(count):
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
long count = numbers.stream()
                   .count();
// 结果:5
  1. 并行处理(parallelStream):
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.parallelStream()
                 .reduce(0, (a, b) -> a + b);
// 结果:15(使用并行流进行求和)

以上仅是一些Stream API的常见使用实例,Stream API还提供了更多的中间操作和终端操作,可以根据具体需求进行灵活应用。

4. 函数式编程接口

是什么

函数式编程接口是一种用于函数式编程的概念,也称为函数接口或单方法接口。它是一个只包含一个抽象方法的接口,用于定义函数的签名(即参数和返回类型),并且可以通过Lambda表达式或方法引用来实现该方法。

组成

在Java中,函数式编程接口由以下组成部分构成:

  1. 函数式方法:函数式编程接口必须包含一个抽象方法,称为函数式方法。这个方法定义了接口的核心行为,用于表示某个特定的功能或行为。函数式方法没有方法体,只有方法签名。
  2. 默认方法(Default Methods):函数式编程接口可以包含默认方法。默认方法是在接口中提供默认实现的方法,用关键字 default 进行修饰。默认方法提供了接口的默认行为,可以在实现类中选择性地重写或直接使用。
  3. 静态方法(Static Methods):函数式编程接口可以包含静态方法。静态方法是与接口相关联的静态方法,用关键字 static 进行修饰。静态方法可以直接通过接口进行调用,不需要实例化接口的实现类。

函数式编程接口通过上述组成部分来定义接口的行为和功能,并提供了一种标准化的方式来表示函数式的行为。这使得函数式编程接口能够与Lambda表达式进行匹配,实现函数式编程的特性。

在Java 8及以上版本中,为了更好地支持函数式编程,引入了 java.util.function 包,其中定义了一些常用的函数式编程接口,如 FunctionPredicateConsumer 等,使得开发者能够更方便地使用函数式编程接口来处理数据和行为。同时,开发者也可以自定义函数式编程接口来满足特定的需求。

一个注解

@FunctionalInterface 是一个函数式编程接口的注解,用于标识接口是否符合函数式接口的规范。它是在Java 8中引入的,用于增强对函数式编程接口的支持和编译时的检查。

@FunctionalInterface 注解的作用和使用方法如下:

  1. 标识函数式接口:@FunctionalInterface注解用于标识接口是否是函数式编程接口。它可以应用于任何接口,但只有满足以下条件的接口才能被标记为函数式接口:
  • 接口中只能有一个抽象方法。
  • 接口可以有默认方法和静态方法。
  • 如果一个接口被标记为 @FunctionalInterface,但实际上不符合函数式接口的定义,编译器将会报错。
  1. 编译时检查:使用 @FunctionalInterface 注解可以让编译器在编译时检查接口是否符合函数式接口的规范。如果接口中包含多于一个的抽象方法,或者没有抽象方法但没有标记为 @FunctionalInterface,编译器将会产生编译错误,以确保接口的正确性。
  2. 增强文档可读性:通过使用 @FunctionalInterface 注解,可以使函数式接口的意图更加清晰明确。其他开发者在使用该接口时,可以明确知道该接口的设计意图,并且可以通过接口的文档注释来了解该接口应该如何使用。
示例

以下是一个示例,展示了一个函数式编程接口的定义,包括 @FunctionalInterface 注解、函数式方法、默认方法和静态方法:

@FunctionalInterface
public interface MyFunctionalInterface {
    void doSomething(); // 函数式方法
    default void doSomethingElse() {
        System.out.println("Doing something else"); // 默认方法
    }
    static void doStaticThing() {
        System.out.println("Doing static thing"); // 静态方法
    }
}

在上述示例中,MyFunctionalInterface 接口被标记为 @FunctionalInterface,它包含一个函数式方法 doSomething(),一个默认方法 doSomethingElse() 和一个静态方法 doStaticThing()

使用示例:

public class Main {
    public static void main(String[] args) {
        // 使用Lambda表达式创建接口的实例
        MyFunctionalInterface functionalInterface = () -> System.out.println("Doing something");
        functionalInterface.doSomething(); // 输出:Doing something
        // 调用默认方法
        functionalInterface.doSomethingElse(); // 输出:Doing something else
        // 调用静态方法
        MyFunctionalInterface.doStaticThing(); // 输出:Doing static thing
    }
}

在上述示例中,我们使用Lambda表达式创建了一个实现了 MyFunctionalInterface 接口的匿名类的实例。然后,我们调用了函数式方法 doSomething(),输出了 “Doing something”。我们还调用了默认方法 doSomethingElse(),输出了 “Doing something else”。最后,我们调用了静态方法 doStaticThing(),输出了 “Doing static thing”。

这个示例展示了一个函数式编程接口的完整定义,包括函数式方法、默认方法和静态方法,并展示了如何使用该接口的实例。

示例

下表列出了Java 8中常用的函数式编程接口、功能描述以及使用示例:

接口名 功能描述 使用示例
Supplier<T> 无参数,返回一个结果 Supplier<Integer> supplier = () -> 42;
Consumer<T> 接受一个参数并且不返回结果 Consumer<String> consumer = (s) -> System.out.println(s);
BiConsumer<T, U> 接受两个参数并且不返回结果 BiConsumer<String, Integer> biConsumer = (s, i) -> System.out.println(s + i);
Function<T, R> 接受一个参数并返回结果 Function<Integer, String> function = (i) -> "Result: " + i;
BiFunction<T, U, R> 接受两个参数并返回结果 BiFunction<String, Integer, String> biFunction = (s, i) -> s + i;
Predicate<T> 接受一个参数并返回布尔值 Predicate<Integer> predicate = (i) -> i > 0;
BiPredicate<T, U> 接受两个参数并返回布尔值 BiPredicate<String, Integer> biPredicate = (s, i) -> s.length() > i;
UnaryOperator<T> 接受一个参数并返回相同类型的结果 UnaryOperator<Integer> unaryOperator = (i) -> i * 2;
BinaryOperator<T> 接受两个参数并返回相同类型的结果 BinaryOperator<Integer> binaryOperator = (a, b) -> a + b;
Supplier 提供一个无参的值 Supplier<Integer> supplier = () -> 42;
Consumer 接受一个参数并处理,无返回值 Consumer<String> consumer = (s) -> System.out.println(s);
BiConsumer 接受两个参数并处理,无返回值 BiConsumer<String, Integer> biConsumer = (s, i) -> System.out.println(s + i);
Function 接受一个参数并返回结果 Function<Integer, String> function = (i) -> "Result: " + i;
BiFunction 接受两个参数并返回结果 BiFunction<String, Integer, String> biFunction = (s, i) -> s + i;
Predicate 接受一个参数并返回布尔值 Predicate<Integer> predicate = (i) -> i > 0;
BiPredicate 接受两个参数并返回布尔值 BiPredicate<String, Integer> biPredicate = (s, i) -> s.length() > i;
UnaryOperator 接受一个参数并返回相同类型的结果 UnaryOperator<Integer> unaryOperator = (i) -> i * 2;
BinaryOperator 接受两个参数并返回相同类型的结果 BinaryOperator<Integer> binaryOperator = (a, b) -> a + b;

以上是一些Java 8中常用的函数式编程接口的示例

,你可以根据需要选择合适的接口来定义和使用函数式行为。

5. 总结

  • 在Java中,函数式编程的实现方式是Lambda表达式语法
  • Stream API是基于函数式编程思想设计的,强调对集合数据进行流式处理和转换。它提供了一系列函数式操作,如映射、过滤、排序、聚合等,使得我们可以以一种更函数式的风格来操作数据。
目录
相关文章
|
13天前
|
人工智能 自然语言处理 API
Multimodal Live API:谷歌推出新的 AI 接口,支持多模态交互和低延迟实时互动
谷歌推出的Multimodal Live API是一个支持多模态交互、低延迟实时互动的AI接口,能够处理文本、音频和视频输入,提供自然流畅的对话体验,适用于多种应用场景。
62 3
Multimodal Live API:谷歌推出新的 AI 接口,支持多模态交互和低延迟实时互动
|
8天前
|
前端开发 API 数据库
Next 编写接口api
Next 编写接口api
|
14天前
|
XML JSON 缓存
阿里巴巴商品详情数据接口(alibaba.item_get) 丨阿里巴巴 API 实时接口指南
阿里巴巴商品详情数据接口(alibaba.item_get)允许商家通过API获取商品的详细信息,包括标题、描述、价格、销量、评价等。主要参数为商品ID(num_iid),支持多种返回数据格式,如json、xml等,便于开发者根据需求选择。使用前需注册并获得App Key与App Secret,注意遵守使用规范。
|
13天前
|
JSON API 开发者
淘宝买家秀数据接口(taobao.item_review_show)丨淘宝 API 实时接口指南
淘宝买家秀数据接口(taobao.item_review_show)可获取买家上传的图片、视频、评论等“买家秀”内容,为潜在买家提供真实参考,帮助商家优化产品和营销策略。使用前需注册开发者账号,构建请求URL并发送GET请求,解析响应数据。调用时需遵守平台规定,保护用户隐私,确保内容真实性。
|
13天前
|
搜索推荐 数据挖掘 API
淘宝天猫商品评论数据接口丨淘宝 API 实时接口指南
淘宝天猫商品评论数据接口(Taobao.item_review)提供全面的评论信息,包括文字、图片、视频评论、评分、追评等,支持实时更新和高效筛选。用户可基于此接口进行数据分析,支持情感分析、用户画像构建等,同时确保数据使用的合规性和安全性。使用步骤包括注册开发者账号、创建应用获取 API 密钥、发送 API 请求并解析返回数据。适用于电商商家、市场分析人员和消费者。
|
23天前
|
JSON API 开发工具
淘宝实时 API 接口丨淘宝商品详情接口(Taobao.item_get)
淘宝商品详情接口(Taobao.item_get)允许开发者获取商品的详细信息,包括基本信息、描述、卖家资料、图片、属性及销售情况等。开发者需注册账号、创建应用并获取API密钥,通过构建请求获取JSON格式数据,注意遵守平台规则,合理使用接口,确保数据准确性和时效性。
|
24天前
|
JSON 缓存 监控
淘宝商品详情接口(Taobao.item_get)丨淘宝API接口指南
淘宝商品详情接口(Taobao.item_get)允许开发者通过HTTP GET方法获取淘宝商品的详细信息,包括商品ID、价格、库存等。请求需包含key、secret、num_iid等必选参数,支持缓存及多种返回格式。此接口广泛应用于电商数据分析、商品选品、价格监控等领域,提升商家运营效率。
|
Java
Java8中stream流处理数据21个小案例(学习使用)
Java8中stream流处理数据21个小案例(学习使用)
101 0
|
SQL 存储 前端开发
【Java技术指南】「Java8技术盲区」在奔向Java13的同时,也让我们仔细研究一下Stream的学习认知!
【Java技术指南】「Java8技术盲区」在奔向Java13的同时,也让我们仔细研究一下Stream的学习认知!
147 0
【Java技术指南】「Java8技术盲区」在奔向Java13的同时,也让我们仔细研究一下Stream的学习认知!
|
Java 程序员 API
Java 8 Stream API学习总结
Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
1041 0