百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

Java 函数式编程:开启高效编程之旅

yuyutoo 2025-02-26 14:27 5 浏览 0 评论

一、引言

Java 函数式编程是 Java 8 引入的重要特性,它为开发者提供了一种更加简洁、高效的编程方式。本文将深入探讨掌握函数式编程在 Java 中的应用。

函数式编程是一种编程范式,强调函数的使用作为编程的主要元素,并鼓励将函数作为参数传递给其他函数,或者将函数作为返回值返回。Java 8 引入了对函数式编程的支持,主要通过 Lambda 表达式和函数接口来实现。

Lambda 表达式是一个匿名函数,允许像对象一样传递它,使得语言更具表现力和简洁性。其语法为 (parameters) -> expression 或者 (parameters) -> { statements; },其中 parameters 是参数列表,箭头 -> 分隔参数列表和表达式或语句块。

函数接口是只包含一个抽象方法的接口。Java 8 提供了 @FunctionalInterface 注解来明确指示一个接口是函数接口。函数接口可以有默认方法和静态方法,但只能有一个抽象方法。

函数式编程在 Java 中的应用场景包括但不限于:

  • 集合操作:使用 Stream API 进行函数式操作,例如映射(map)、过滤(filter)、归约(reduce)等。
  • 并发编程:使用并行流(Parallel Streams)提高多核处理器的利用率。
  • 事件驱动:通过函数式接口处理事件和回调。
  • 简化代码:Lambda 表达式和方法引用能够减少样板代码,使代码更加清晰和易读。

二、函数式编程基础概念

1. 函数是第一等公民

在函数式编程范式中,函数如同语言中的第一等公民,可以被创建、引用、作为参数传递等。函数式编程将函数提升到与其他数据类型同等的地位,就像整数、字符串等基本数据类型一样。例如,在 Java 中,我们可以将函数赋值给变量,如同 var print = function (i){ console.log (i);};[1,2,3].forEach (print); 这里的 print 变量就是一个函数,可以作为另一个函数的参数。

同时,在 Java 8 引入的 Lambda 表达式更是体现了函数作为第一等公民的特性。Lambda 表达式允许像对象一样传递它,使得语言更具表现力和简洁性。例如,在 Java 中,我们可以将 Lambda 表达式传递给各种方法和构造函数,如 Thread thread = new Thread (() -> System.out.println ("In another thread")); 这里的 Lambda 表达式就作为参数传递给了 Thread 类的构造函数。

2. 纯函数

满足执行无副作用且返回值仅取决于输入参数的函数被称为纯函数,纯函数有助于提高代码的可预测性和可维护性。

纯函数具有以下特点:

  • 纯函数的返回值完全取决于它的参数,因此,如果使用相同的参数集调用纯函数,则始终会获得相同的返回值。例如,public int add (int x,int y){return x + y;} 这个 add 函数就是一个纯函数,每次调用都会返回相同的输出结果。
  • 纯函数没有任何副作用,如网络或数据库调用。它们不会修改传递给它们的参数,也不会修改全局变量或系统状态。例如,public List filter(List list,Predicate predicate){List result = new ArrayList<>();for (Integer i : list){if (predicate.test (i)){result.add (i);}} return result;} 这个 filter 函数就是一个纯函数,它不依赖于任何外部变量或状态,每次调用都会返回相同的输出结果。

与之相反,非纯函数的返回值不仅仅取决于它的参数,可能会有副作用,如网络或数据库调用,或者修改传递给它们的参数。例如,public int addWithSideEffect (int x,int y){int result = x + y;System.out.println ("The result is:"+ result);return result;} 这个 addWithSideEffect 函数就不是一个纯函数,因为它还有一个副作用,即在计算结果之后输出一条消息。

纯函数具有引用透明性,即如果一个函数对于相同的输入始终产生相同的结果,那么我们就说它是引用透明。纯函数的这些特性使得它们在保证代码的可测试性、可读性和可维护性方面具有很大的优势。纯函数可以自由地组合在一起,形成更复杂的函数,而不需要考虑它们的执行顺序或副作用的影响。这种组合性使得代码更加模块化、可复用性更高,也更容易进行测试和维护。由于纯函数的输出结果只取决于输入参数,因此可以将函数的输入参数作为键存储到缓存中,这样在下次调用时就可以直接返回缓存中的结果,而不必重新计算。这种缓存技术可以提高函数的性能,同时也可以避免因为重复计算产生的副作用。

三、Java 中的函数式接口

1. 定义与特点

在 Java 中,函数式接口是指只有一个抽象方法的接口。这种接口可以通过 Lambda 表达式实现,编译器会根据 Lambda 表达式的参数和返回值推断其实现的接口。

函数式接口具有以下特点:

  • 简洁性:使用 Lambda 表达式实现函数式接口可以使代码更加简洁,减少样板代码的编写。
  • 可读性:Lambda 表达式的简洁性也提高了代码的可读性,使代码更容易理解。
  • 灵活性:函数式接口可以作为方法的参数或返回值,使代码更加灵活。

2. 常见内置函数式接口

Function 接口

Java 中的 Function 接口是一个函数式接口,它接受一个输入参数,并且返回一个结果。Function 接口通常用于将一个对象或值转换成另一个对象或值,例如对数据进行加密、解密、格式化等场景。

定义接口对象:Function function = (num) -> "result: " + num;。这个示例代码定义了一个 Function 接口对象,它接受一个整数类型的参数,并返回一个字符串类型的结果,将整数转换成字符串形式并添加前缀 "result:"。

使用接口对象:String result = function.apply(123);System.out.println(result);。在这个示例代码中,我们通过调用 apply () 方法来将一个整数传递给 Function 接口对象,并获得一个字符串作为结果。输出结果为 "result: 123"。

Function 接口还可以使用方法引用(Method Reference)来简化代码,例如:Function function = String::valueOf;。这行代码等价于上面的示例代码,将 String.valueOf () 方法作为 Function 接口对象的实现。

Function 接口还可以使用其它函数式接口一起使用,例如 Consumer、Predicate 等,以实现复杂的业务逻辑。下面是几个示例:

//对字符串列表进行处理:
List list = Arrays.asList("apple", "banana", "cherry");
Function function = (str) -> str.length();List result = new ArrayList<>();
for (String str : list) {
  int len = function.apply(str);result.add(len);
                        }
System.out.println(result);
// 输出每个字符串的长度。这个示例代码使用 Function 接口对一个字符串列表进行处理,将每个元素转换成它的长度,并将结果保存到另一个列表中。
//将一种类型的对象转换成另一种类型:
List persons = Arrays.asList(new Person("Alice"), new Person("Bob"), new Person("Charlie"));
Function function = (person) -> new Student(person.getName())
;List result = new ArrayList<>();
for (Person person : persons) {
  Student student = function.apply(person);result.add(student);
}
System.out.println(result); 
// 将所有人物转换成学生并输出。这个示例代码使用 Function 接口将一个人物列表中的每个人物对象转换成对应的学生对象,并将结果保存到另一个列表中。

总之,Function 接口是 Java 中非常实用的一个函数式接口,在需要将一个对象或值转换成另一个对象或值的场景下特别有用。它可以和其它函数式接口一起使用,方便地实现复杂的业务逻辑。

Supplier 接口

在 Java 8 中,引入了函数式接口的概念,使得我们可以更方便地使用函数式编程范式。其中一个函数式接口是 Supplier,它用于提供值或对象,以供其他方法或函数进一步处理。

概述:Supplier 接口是一个泛型接口,定义了一个不接受参数但返回一个指定类型结果的抽象方法 get ()。该接口可用于替代那些不接受任何参数但需要返回结果的方法,或者作为惰性计算的手段。

基本用法:以下是一个简单的示例,展示了如何使用 Supplier 接口创建随机数生成器:Supplier randomGenerator = () -> new Random().nextInt(100);int randomNumber = randomGenerator.get(); // 生成一个[0,100)范围内的随机数。Supplier 接口可以与 Lambda 表达式结合使用,以提供一个自定义的计算逻辑,并返回所需的值。

延迟获取数据:Supplier 接口也可以作为方法的参数,以延迟获取数据的目的。这在某些情况下非常有用,例如传递昂贵的计算或缓存结果。以下是一个示例,演示了如何将 Supplier 作为方法参数,以实现延迟计算:

public class MyClass {
 private int value;
 public void setValue(int value) {
 this.value = value;
 }
 public int getValue(Supplier supplier) {
 return supplier.get() + value;
 }
}
MyClass obj = new MyClass();
obj.setValue(10);
int result = obj.getValue(() -> 20); // result = 10 + 20 = 30

在这个例子中,我们定义了一个名为 getValue 的方法,它接受一个 Supplier 类型的参数。当需要获取值时,调用 Supplier 的 get () 方法即可动态地获取所需的值。通过这种方式,我们可以推迟计算直到真正需要结果。

更高级的用法:除了上述基本用法外,Supplier 还可以与其他 Java 函数式接口结合使用,如 Function 和 Predicate 等,以实现更复杂的逻辑。例如,我们可以使用 Supplier 和 Function 来创建一个可重试的方法调用机制:

public  T retry(Supplier supplier, Predicate retryCondition, int maxAttempts) {
 int attempts = 0;
 while (attempts < maxAttempts) {
 T result = supplier.get();
 if (retryCondition.test(result)) {
 attempts++;
 } else {
 return result;
 }
 }
 throw new RuntimeException("Max attempts reached");
}
// 使用示例
Supplier apiCall = () -> // 调用 API 接口的代码
Predicate retryCondition = // 重试条件的判断逻辑
int result = retry(apiCall, retryCondition, 3); // 最多重试 3 次

在这个例子中,我们定义了一个 retry 方法,它接受一个 Supplier 作为 API 调用,一个 Predicate 来判断是否需要重试,以及最大尝试次数。通过结合 Supplier 和 Predicate 的使用,我们实现了一个可重试的方法调用机制。

总结:通过本篇博客,我们深入了解了 Java 中 Supplier 接口的基本用法和更高级的应用场景。Supplier 接口可以用于提供值或对象,也可以作为方法参数延迟获取数据。同时,它还可以与其他函数式接口结合使用,以实现更复杂的逻辑。通过掌握 Supplier 的使用,我们可以更加灵活地编写函数式风格的代码,从而提高代码的可读性和可维护性。希望本篇博客能够帮助你理解 Supplier 接口,并在实际工作中发挥其强大的功能。如果你有任何问题或建议,欢迎在评论区留言,让我们一起探讨和学习!

Consumer 接口

Java 中的 Consumer 接口是一个函数式接口,用于表示接受单个输入参数并不返回结果的操作。它通常用于执行一些操作,比如对参数进行处理、打印输出或者进行其他副作用。

Consumer 接口定义了一个名为 accept 的抽象方法,该方法接受一个参数并执行操作,不返回任何结果。除了抽象方法之外,Consumer 接口还包含了一个默认方法,用于函数的组合、转换和操作。

以下是一个简单的示例,演示了如何使用 Consumer 接口:

public static void consumer_test(){
 Consumer addOne = x ->{x = x + 1;System.out.println("addOne:" + x);};
 addOne.accept(2);
 Consumer multiplyByTwo = x ->{x = x * 2;System.out.println("multiplyByTwo:" + x);};
 addOne.andThen(multiplyByTwo).accept(2);
}

从结果中可以看出 andThen 方法是将两个方法组合成了一个方法,然后调用方法是参数初始值是同一个,但不是同一个对象。

同样的,在 java.util.function 包下面看到许多其它类似的函数接口,原理也差不多。

Predicate 接口

Predicate 接口是 Java 中的一个函数式接口,它代表着一个断言(即一个布尔类型的函数),接受一个参数并返回一个布尔值。在 Predicate 接口中,通常会定义一个名为 test 的抽象方法,用于对给定的参数进行条件判断。

Predicate 接口中定义了几个方法,这些方法可以帮助你对多个 Predicate 实例进行组合、取反等操作。以下是 Predicate 接口中的一些主要方法:

test(T t):这是 Predicate 接口中的抽象方法,用于对给定的参数进行条件判断。它接受一个参数并返回一个布尔值,表示参数是否满足条件。

四、Lambda 表达式

1. 语法结构

Lambda 表达式由参数列表、箭头符号和代码块组成,其语法简洁且灵活。例如,(parameters) -> expression或者(parameters) -> { statements; },其中 parameters 是参数列表,箭头->分隔参数列表和表达式或语句块。无需声明参数类型时,编译器可以自动识别参数值;一个参数无需定义圆括号,但多个参数需要;如果主体包含一个语句,就不需要使用大括号;如果主体只有一个表达式返回值,编译器会自动返回值,大括号需指定明表达式返回了一个数值。

2. 使用场景

  • 作为函数参数:在 Java 中,Lambda 表达式常常用于实现函数作为参数传递的功能。Java 的函数式接口是只有一个抽象方法的接口,由于 Lambda 表达式可以用来实例化这样的接口,所以它们可以作为参数传递。例如,定义一个函数式接口NumberOperation,有一个apply方法接受两个 int 参数并返回一个 int 结果,performOperation方法接受两个 int 参数和一个NumberOperation类型的参数,并调用该函数的apply方法。在main方法中,创建不同的 Lambda 表达式实例,每个实例都实现了NumberOperation接口,然后将这些 Lambda 表达式作为参数传递给performOperation方法,并打印出结果。
  • 线程:Lambda 表达式可以用于简化多线程编程。通过 Lambda 表达式可以更方便地创建线程、定义线程的执行逻辑、实现线程之间的通信等。例如,Runnable runnable = () -> System.out.println("In another thread");,这里的 Lambda 表达式就作为参数传递给了Thread类的构造函数。
  • 事件处理程序等:在 GUI 编程中,Lambda 表达式可以简化事件处理的代码。例如,按钮点击事件处理,button.addActionListener(e -> System.out.println("Button clicked."));。
  • 具有简洁性、灵活性和高性能等优点
    • 简洁性:Lambda 表达式提供了一种更为简洁的语法,尤其适用于函数式接口。相比于传统的匿名内部类,Lambda 表达式使得代码更为紧凑,减少了样板代码的编写。如传统的匿名内部类实现Runnable接口,Runnable runnable1 = new Runnable() { @Override public void run() { System.out.println("Hello World!"); }};,而使用 Lambda 表达式为Runnable runnable2 = () -> System.out.println("Hello World!");。
    • 灵活性:Lambda 表达式可以访问外部作用域的变量,这种特性称为变量捕获,Lambda 表达式可以隐式地捕获 final 或事实上是 final 的局部变量。例如,int x = 10;MyFunction myFunction = y -> System.out.println(x + y);myFunction.doSomething(5);。
    • 高性能:Lambda 表达式能够更方便地实现并行操作,通过使用 Stream API 结合 Lambda 表达式,可以更容易地实现并行计算,提高程序性能。例如,List numbers = Arrays.asList(1,2, 3, 4, 5);int sum = numbers.parallelStream().mapToInt(Integer::intValue).sum();。

五、函数式编程在 Java 中的好处

1. 减少业务逻辑和代码分歧

在 Java 中,函数式编程有助于减少业务逻辑和代码的分歧。传统的面向对象编程方式往往更侧重于实现细节,而函数式编程则允许我们在更高层次上更自然地描述业务逻辑,使代码直接聚焦于 “你想做什么”,而非 “你想怎样去做”。

通过函数式编程,许多样板代码可以被移除,从而让代码更加清晰简洁。例如,使用 Lambda 表达式和函数式接口可以简化集合操作、线程创建、事件处理等常见编程任务,减少了重复的代码编写。

2. 高阶函数的优势

高阶函数在 Java 中具有显著的优势。它允许我们发送、创建和返回方法,为代码带来了更高的健壮性、集中性和可重用性。

高阶函数可以接收其他函数作为参数,这使得我们能够将方法传递到其他方法中,实现更灵活的代码组合。例如,在 Java 的流式计算中,高阶函数如 map、reduce 和 filter 接收函数作为参数,对集合进行各种操作,大大提高了代码的可扩展性。

我们还可以在其他方法中创建方法,通过高阶函数返回方法,进一步增强了代码的灵活性。这种方式使得我们能够根据不同的需求动态地生成和返回函数,实现更加复杂的业务逻辑。

3. Lambda 表达式的好处

Lambda 表达式为 Java 带来了诸多好处。首先,它实现了惰性求值,即当 Lambda 表达式作为方法的参数时,Java 编译器会在方法中实际调用该表达式时才计算其值,与一般方法参数直接求值的方式不同。

Lambda 表达式让单元测试更加有趣。它允许我们创建简洁、小巧和快速的轻量级单元测试,并且能够储存测试代码,方便我们测试各种场景对代码的影响,探索新的模式。

例如,使用 Lambda 表达式可以更简洁地定义测试用例,无需编写冗长的匿名内部类。同时,我们可以轻松地组合和修改 Lambda 表达式,以适应不同的测试场景。

六、Java 函数式编程应用

1. 缓求值

通过 Supplier 实现惰性求值,节省系统资源,提升性能。在 Java 中,Supplier 接口可以用于实现缓求值。例如在判断工作台是否为标准或默认工作台的场景中,使用 Supplier 可以定义一个匿名表达式,只有在特定条件下才进行求值,避免不必要的计算。如以下代码所示:

public boolean isStandardOrDefaultWorkbench(SchemaContext context) {
 Supplier isDefaultWorkbench = () -> StringUtils.isNotBlank(context.getDefaultAppUuid()) && StringUtils.equals(context.getAppUuid(), context.getDefaultAppUuid());
 return WorkbenchType.WORKBENCH.equals(context.getWorkbenchType()) || isDefaultWorkbench.get();
}

通过缓求值的方式,可以节省在某些情况下的消耗,提升系统性能,节省资源。

2. 高阶函数

以 Stream 为例,展示高阶函数在处理集合时的强大功能,可传递 Lambda 表达式处理业务逻辑。

高阶函数是指使用其他函数作为参数、或者返回一个函数作为结果的函数。在 Java 中,Stream 就是典型的流处理思想,可以通过 Stream 来处理集合并执行相关的操作,其中 Stream 包含了大量的高阶函数,我们可以直接使用匿名表达式(Lambda 表达式)来传递业务逻辑。

例如,过滤隐藏应用的代码如下:

// 过滤隐藏应用
return appDetails.stream().filter(app -> {
 AppModel appModel = new AppModel(app.getAppId(), app.getAppType());
 return!appDisplayModel.getHideApps().contains(appModel);
}).collect(Collectors.toList());

从上面的逻辑可以看到,我们可以通过集合的 Stream 对象进行 filter、map、collect 等操作,其中这些高阶函数就可以传递 lambda 表达式,用于处理我们需要的逻辑。

除了 Stream 之外,Java 很多工具类也支持外部传入 lambda,比如 Optional。以下是一个获取组织默认工作台 appUuid 的例子:

public String getDefaultWorkbenchAppUuid(String corpId, OrgConfigTag orgConfigTag) {
 return Optional.ofNullable(orgConfigTag).map(OrgConfigTag::getDefaultAppUuid).orElseGet(() -> OpenPageUtils.buildWorkbenchAppUuid(corpId));
}

其中的 orElseGet 方法就支持外部传入 lambda 表达式。

3. 函数回调

可用于分离不同模块的逻辑,如分布式锁控制中通过 lambda 回调实现加锁和业务逻辑分离。

在计算机程序设计中,回调函数是指通过参数将函数传递到其它代码的,某一块可执行代码的引用。函数回调可用于接口两块完全无关的逻辑,比如在 A 模块执行完毕后,执行 B 模块,B 模块的代码事先在 A 模块注册。

很多框架型的逻辑,比如统一结果处理、缓存框架、分布式锁、线程缓存等都可以通过这个方式来优化,就可以使用 lambda 的方式来实现。核心的逻辑就是先处理相关的通用的中间件逻辑,然后通过 lambda 来执行业务逻辑。

例如,对于要实现分布式锁控制的模块,可以使用 lambda 的回调,来实现加锁和业务逻辑的分离。以下是相关代码:

protected void createMyPage(String corpId, Long uid, String appUuid, Method method) throws WorkbenchException {
 // 并发锁控制
 distributeLockSupport.lockAndDo(key, () -> {
 //创建页面方法
 userCorpPageSupport.createMyPage(corpId, uid);
 return null;
 });
}

七、Java 函数式编程最佳实践

1. 简化事务

使用函数式编程抽去写逻辑,使事务粒度更小,读写逻辑分开。在实际的业务开发中,我们可以将复杂的业务逻辑拆分为多个小的函数,每个函数只负责一个特定的任务。这样可以提高代码的可读性和可维护性,同时也使得代码的测试更加容易。例如,在处理用户注册的业务逻辑时,可以将用户信息的验证、用户数据的保存等操作分别封装在不同的函数中,然后在主流程中依次调用这些函数,实现事务的处理。

2. 赋予方法重试能力

通过自定义函数接口实现方法的重试功能。在 Java 中,我们可以使用自定义的函数接口来实现方法的重试功能。例如,可以定义一个ThrowExceptionRunnable接口,该接口包含一个run方法,用于执行需要重试的操作。然后,可以定义一个retryFunction方法,该方法接受一个ThrowExceptionRunnable接口的实现对象和一个重试次数作为参数,在方法内部使用循环来执行需要重试的操作,如果操作失败,则递减重试次数并继续尝试,直到操作成功或者重试次数耗尽。

3. 赋予函数缓存能力

利用缓存提高函数的执行效率。在 Java 函数式编程中,可以通过使用缓存来提高函数的执行效率。例如,可以定义一个cacheFunction方法,该方法接受一个Function接口的实现对象、一个输入参数和一个缓存Map作为参数。在方法内部,首先检查缓存中是否已经存在输入参数对应的结果,如果存在,则直接返回缓存中的结果;如果不存在,则调用Function接口的实现对象,计算结果并将结果存入缓存中,然后返回结果。

4. 赋予函数报错返回默认值能力

在函数出错时返回默认值,增强程序的健壮性。在 Java 函数式编程中,可以通过定义一个computeOrGetDefault方法来实现函数报错返回默认值的能力。该方法接受一个可能抛出异常的函数和一个默认值作为参数,如果函数执行成功,则返回函数的结果;如果函数执行失败,则返回默认值。这样可以在函数出现异常时,提供一个默认的返回值,增强程序的健壮性。

5. 赋予函数处理异常的能力

处理函数执行过程中的异常,返回特定结果。在 Java 函数式编程中,可以通过定义一个computeAndDealException方法来实现函数处理异常的能力。该方法接受一个可能抛出异常的函数、一个输入参数和一个处理异常的函数作为参数。如果函数执行成功,则返回函数的结果;如果函数执行失败,则调用处理异常的函数,将异常作为参数传递给该函数,并返回处理异常的函数的结果。

6. 赋予函数记录日志能力

记录函数的执行过程,方便调试和性能分析。在 Java 函数式编程中,可以通过定义一个logFunction方法来实现函数记录日志的能力。该方法接受一个Function接口的实现对象、一个输入参数和一个日志标题作为参数。在方法内部,首先记录函数的开始执行时间和输入参数,然后调用Function接口的实现对象,计算结果并记录函数的结束执行时间和结果,最后返回结果。这样可以方便地记录函数的执行过程,方便调试和性能分析。

八、Java 函数式编程实例

1. 函数式接口声明与使用

在 Java 中,函数式接口是指只有一个抽象方法的接口。可以通过 Lambda 表达式实现函数式接口中的抽象方法。例如,定义一个简单的函数式接口MyFunctionInterface:

@FunctionalInterface
interface MyFunctionInterface {
 void myMethod();
}

然后可以使用 Lambda 表达式来实现这个接口:

MyFunctionInterface myFunction = () -> System.out.println("This is implemented using Lambda expression.");
myFunction.myMethod();

2. Java Stream 函数式编程案例

Java Stream 是 Java 8 引入的一个重要特性,它提供了一种高效的方式来处理集合数据。以下是以处理集合数据为例,介绍 Java Stream 的过滤、映射、排序等功能。

过滤(filter)

filter是 Java 8 中 Stream API 提供的一个方法,用于过滤 Stream 中的元素。在 Stream 中使用 filter 方法时,可以通过 Lambda 表达式定义一个过滤条件,只有符合该条件的元素才会被保留下来,而不符合条件的元素会被过滤掉。例如,假设有一个名为list的 List,其中包含一些整数,如果要将其中大于 10 的整数筛选出来,可以使用以下代码:

List filteredList = list.stream().filter(i -> i > 10).collect(Collectors.toList());

映射(map)

Stream API 提供了一个map方法,用于将一个 Stream 中的元素进行映射转换。map方法会对 Stream 中的每个元素应用一个函数,返回一个新的 Stream,该 Stream 中的元素是映射后的结果。例如:

List list = Arrays.asList(1, 2, 3, 4, 5);

List result = list.stream().map(n -> n * n).collect(Collectors.toList());

排序(sorted)

Stream API 提供了一个sorted方法,用于将 Stream 中的元素进行排序。默认情况下,sorted是按照自然顺序进行排序。如果需要自定义排序规则,可以传入一个Comparator。例如:

List strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
strList.stream().sorted().forEach(System.out::println);
emps.stream().sorted((x, y) -> {
 if (x.getAge() == y.getAge()) {
 return x.getName().compareTo(y.getName());
 } else {
 return Integer.compare(x.getAge(), y.getAge());
 }
}).forEach(System.out::println);

归约(reduce)

Stream API 提供了一个reduce方法,用于将 Stream 中的元素进行归约操作。reduce方法接受一个BinaryOperator函数作为参数,该函数用于将 Stream 中的元素两两结合,得到一个新的结果,然后将该结果再与下一个元素结合,以此类推,最终得到一个归约后的结果。例如:

List list = Arrays.asList(1, 2, 3, 4, 5);
int sum = list.stream().reduce(0, (a, b) -> a + b);

分组(groupingBy)

Stream API 提供了一个Collectors工具类,其中包含了许多用于 Stream 的归约操作的方法,其中之一就是groupingBy方法。groupingBy方法用于按照某个属性对 Stream 中的元素进行分组,并将分组后的元素放入一个Map中。例如:

List persons = Arrays.asList(new Person("Alice", "London"), new Person("Bob", "New York"), new Person("Charlie", "London"), new Person("David", "New York"));
Map> result = persons.stream().collect(Collectors.groupingBy(Person::getCity));

九、结论

Java 函数式编程为开发者带来了诸多好处和强大的应用场景,掌握函数式编程将极大地提高 Java 开发效率和代码质量。

函数式编程在 Java 中的应用不仅体现在简洁高效的编程方式上,还能通过各种特性和接口提升代码的可读性、可维护性和可扩展性。

从 Lambda 表达式的灵活语法到函数式接口的多样应用,再到 Stream API 的强大功能,Java 函数式编程为开发者提供了丰富的工具和方法。

在实际开发中,函数式编程可以减少业务逻辑和代码的分歧,通过高阶函数和 Lambda 表达式的优势,实现更健壮、集中且可重用的代码。同时,函数式编程的最佳实践和实例展示了如何更好地运用这一编程范式,提高代码的质量和效率。

总之,掌握 Java 函数式编程对于现代 Java 开发者来说至关重要

相关推荐

网站建设:从新手到高手

现代化网站应用领域非常广泛,从个人形象网站展示、企业商业网站运作、到政府公益等服务网站,各行各业都需要网站建设。大体上可以归结四类:宣传型网站设计、产品型网站制作、电子商务型网站建设、定制型功能网站开...

JetBrains 推出全新 AI 编程工具 Junie,助力高效开发

JetBrains宣布推出名为Junie的全新AI编程工具。这款工具不仅能执行简单的代码生成与检查任务,还能应对编写测试、验证结果等复杂项目,为开发者提供全方位支持。根据SWEBench...

AI也能写代码!代码生成、代码补全、注释生成、代码翻译轻松搞定

清华GLM技术团队打造的多语言代码生成模型CodeGeeX近期更新了新的开源版本「CodeGeeX2-6B」。CodeGeeX2是多语言代码生成模型CodeGeeX的第二代模型,不同于一代CodeG...

一键生成前后端代码,一个36k星的企业级低代码平台

「企业级低代码平台」前后端分离架构SpringBoot2.x,SpringCloud,AntDesign&Vue,Mybatis,Shiro,JWT。强大的代码生成器让前后端代码一键生成,无需写任...

Gitee 代码托管实战指南:5 步完成本地项目云端同步(附避坑要点)

核心流程拆解:远程仓库的搭建登录Gitee官网(注册账号比较简单,大家自行操作),点击“新建仓库”,建议勾选“初始化仓库”和“设置模板文件”(如.gitignore),避免上传临时文件。...

jeecg-boot 源码项目-强烈推荐使用

JEECGBOOT低代码开发平台...

JetBrains推出全新AI编程工具Junie,强调以开发者为中心

IT之家2月1日消息,JetBrains发文,宣布推出一款名为Junie的全新AI编程工具,官方声称这款AI工具既能执行简单的代码生成与检查等基础任务,也能应对“编写测试、验证结...

JetBrains旗下WebStorm和Rider现已加入“非商用免费”阵营

IT之家10月25日消息,软件开发商JetBrains今日宣布,旗下WebStorm(JavaScript开发工具)和Rider(.NET开发工具)现已加入“非商用免费”阵营。如果...

谈谈websocket跨域

了解websocketwebsocket是HTML5的新特性,在客户端和服务端提供了一个基于TCP连接的双向通道。...

websocket调试工具

...

利用webSocket实现消息的实时推送

1.什么是webSocketwebSocket实现实现推送消息WebSocket是HTML5开始提供的一种在单个TCP连接上进行全双工通讯的协议。以前的推送技术使用Ajax轮询,浏览器需...

Flutter UI自动化测试技术方案选型与探索

...

为 Go 开发的 WebSocket 库

#记录我的2024#...

「Java基础」Springboot+Websocket的实现后端数据实时推送

这篇文章主要就是实现这个功能,只演示一个基本的案例。使用的是websocket技术。...

【Spring Boot】WebSocket 的 6 种集成方式

介绍...

取消回复欢迎 发表评论: