java8函数式编程数据流和lambda表达式

     2022-03-18     267

关键词:

JDK 1.8中引入了函数式编程(functional programming,FP),如果您已习惯OOP,一定会感到困惑:什么是函数式编程?这样的编程模式有什么好处?

本文将通过简单的实例令读者对函数式编程有一个大体的了解。

我们知道OOP是以类为基础的,程序中必须首先抽象和定义class。那么FP创建的基础是什么?或者说在Java 8中,至少需要了解什么知识点才能实现基本的函数式编程呢?

本文将首先介绍在Java 8中使用FP所需的基本知识点:

  • Lambda表达式

  • 数据流

基本实例

Map<String, List<String>> phoneNumbers = new HashMap<String, List<String>>();

phoneNumbers.put("Zhang Jin", Arrays.asList("3232312323", "8933555472"));
phoneNumbers.put("Li Ming", Arrays.asList("12323344", "492648333"));
phoneNumbers.put("Li Guoping", Arrays.asList("77323344", "938448333"));

Map<String, List<String>> filteredNumbers = phoneNumbers.entrySet().stream()
    .filter(x -> x.getKey().contains("Li"))
    .collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));

filteredNumbers.forEach((key, value) -> {
    System.out.println("Name: " + key + ": ");
    value.forEach(System.out::println);
});


上半部分的代码创建了一个从人名到此人所有电话号码的Map,比较简单,但接下来的部分对刚接触Java 8的读者不是一眼就能理解,我们一会儿来详细解释一下。

数据状态不变原则

在分析上面的code前,先来看看FP与我们所熟知的OOP的最大不同。

在面向对象编程时,我们定义类和对象,并使用方法或表达式来执行命令。这些方命令通常会改变程序的数据状态:

Integer x = 0;
x++;

比如上面的代码,当执行x++后,x的值产生了变化,旧数据被新数据所取代。

相反,在函数式编程中,尽管我们也需要通过方法来执行命令,但命令本身并不会改变程序已有的数据。简单地说就是函数调用对外界不产生副作用,并总是输出新的变量作为调用结果,比如下面的javascript:

function max(a, b) {
    return a > b ? a : b;
}

var x = 10;
var y = 5;
var maximum = max(x, y);

此处max并不改变和依赖于外部的x和y,同时每次返回的结果都是一个全新的变量。

代码分析

现在我们回到之前的代码:

// create a map, filter it by key values
Map <String, List<String>> filteredNumbers = phoneNumbers.entrySet().stream()
    .filter(x -> x.getKey().contains("Li"))
    .collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));

这段代码首先调用entrySet()来获得phoneNumbers的entries集合,其中每个entry都由一个键值对组成。

接着,在得到的集合上调用stream()方法,该方法会创建一个数据流 (java.util.stream.Stream)。数据流(stream)是Java 8引入的新概念,参考Javadoc,可以将它理解为是一种可以在其上执行顺序或并行聚合操作的数据序列。这些操作可以包括过滤,修改,不同类型的转换等。

得到stream后,代码调用了filter(),filter通过给定的条件来过滤数据。此处的过滤条件是:

x -> x.getKey().contains("Li")

该处采用了Java 8引入的lambda表达式,我们可以把lambda表达式看成一个简洁的匿名函数,->左边的x是输入参数,->右边是需要执行的命令。这里的lambda表达式又被称作predicate,它是一个返回boolean类型的函数,stream中的每个element都会被代入该predicate,通过运算出的结果来决定是否在filter()重新返回的stream中被留下或移除。

下一个方法是collect(),可以看到,这里的方法调用都是链式的,原因在于这些方法都归属于Stream接口,所以使用起来非常方便简洁。

collect()方法的用途很简单,它先从stream内获得数据(这里是java.util.Map.Entry),再将数据转变成通常的java collection,比如List, Map, Set等数据结构。这样就重新将stream转变成了我们所熟悉的类型。

Collection的遍历

来看最后一部分代码:

filteredNumbers.forEach((key, value) -> {
    System.out.println("Name: " + key + ": ");
    value.stream().forEach(System.out::println);
});

该代码打印出所有的结果,但并没有使用循环,而是用了Java 8引入的forEach方法。

有了前面的基础,可以很容易看懂:forEach接收一个lambda表达式,filteredNumbers的每个element都讲执行该表达式。由于filteredNumbers是一个map,所以这里lambda表达式参数变成了key, value pair:

// expression takes two parameters
(key, value) - > {
    // print person’s name
    System.out.println("Name: " + key + ": ");
    // iterate over the person’s phone numbers and print each of them
    value.forEach(System.out::println);
}

由于value本身是一个List,所以又在其上调用了forEach。

至此代码的输出为:

Name: Li Ming:
12323344
492648333
Name: Li Guoping:
77323344
938448333

学到的知识点 (以及问题)

本文表述的几个关键点:

  • Java 8通过数据流和lambda表达式使函数式编程成为可能。

  • Lambda表达式和匿名函数十分相似。

  • Stream是一种可以在其上执行顺序或并行聚合操作的数据序列,这些操作会作用于序列里的每个元素。

  • 函数式编程模式中的操作倾向于不依赖和不改变已有的数据状态。

  • 数据流操作很简洁性 — 如果不考虑执行效率的话。

但现在您可能会想:

  • 函数式编程写出来的东西理解起来似乎更困难 — 我还是喜欢原来的写法, 尽管它要啰嗦很多。

  • streams操作的执行效率高吗?

  • 如果不改变数据状态,程序应该怎么写才好呢?

这些问题我会在后续的章节中进一步讨论,尽情期待。


本文出自 “Bug之家” 博客,转载请与作者联系!

java8函数式编程

基本概念•Lambda表达式是一个匿名方法,将行为像数据一样进行传递。•Lambda表达式的常见结构:BinaryOperator<Integer>add=(x,y)→x+y。•函数接口指仅具有单个抽象方法的接口,用来表示Lambda表达式的类型。filter:过滤作... 查看详情

java8函数式编程

基本概念•Lambda表达式是一个匿名方法,将行为像数据一样进行传递。•Lambda表达式的常见结构:BinaryOperator<Integer>add=(x,y)→x+y。•函数接口指仅具有单个抽象方法的接口,用来表示Lambda表达式的类型。filter:过滤作... 查看详情

读java8函数式编程笔记08_测试调试和重构

1. Lambda表达式的单元测试1.1. 单元测试是测试一段代码的行为是否符合预期的方式1.2. Lambda表达式没有名字,无法直接在测试代码中调用1.2.1. 将Lambda表达式放入一个方法测试,这种方式要测那个方法,而不是Lambda... 查看详情

java8函数式编程(代码片段)

Lambda表达式函数式接口方法引用与构造器引用StreamAPI接口中的默认方法与静态方法新时间日期API其他新特性速度更快代码更少(增加了新的语法Lambda表达式)强大的StreamAPI便于并行最大化减少空指针异常Optional其中最为核... 查看详情

java8函数式接口编程lambda表达式functionalinterface注解supplierconsumerpredicatefunction函数式接口(代码片段)

1、函数式接口的定义2、FunctionalInterface注解3、函数式接口的使用4、lambda延迟执行特性5、Supplier函数式接口6、Consumer函数式接口7、Predicate函数式接口8、Function函数式接口1、函数式接口的定义有且仅有一个抽象方法的接口函数式... 查看详情

java8函数式编程

函数式编程一、Lambda表达式一)如何辨别Lambda表达式1RunnablenoArguments=()->System.out.println("HelloWorld");23ActionListeneroneArgument=event->System.out.println("buttonclicked");4RunnablemultiStatement=()->{5S 查看详情

跟上java8–了解lambda

...用lambda,带你体验函数式编程的魔力。什么是lambda?lambda表达式是一段可以传 查看详情

java8新特性之lambda(代码片段)

...8-the-missing-tutorialJava8中最重要的特性之一就是引入了lambda表达式。这能够使你的代码更加简练,并允许你将行为传递到各处。一段时间以来,Java因为自身的冗长和缺少函数式编程的能力而受到批评。随着函数式编程变得... 查看详情

java8函数式编程(代码片段)

Lambda表达式函数式接口方法引用与构造器引用StreamAPI接口中的默认方法与静态方法新时间日期API其他新特性速度更快代码更少(增加了新的语法Lambda表达式)强大的StreamAPI便于并行最大化减少空指针异常Optional其中最为核... 查看详情

《java8函数式编程》读书笔记

 Lambda表达式Lambda表达式样例例2-3 编写Lambda表达式的不同形式RunnablenoArguments=()->System.out.println("HelloWorld"); ActionListeneroneArgument=event->System.out.println("buttonclicked");  查看详情

《java8函数式编程》读书笔记

 Lambda表达式Lambda表达式样例例2-3 编写Lambda表达式的不同形式RunnablenoArguments=()->System.out.println("HelloWorld"); ActionListeneroneArgument=event->System.out.println("buttonclicked");  查看详情

java8函数式编程:lambda表达式类型与常用函数接口(代码片段)

...程风格的代码无处不在,所以得要好好学一下了。2Lambda表达式类型无参数:RunnablenoArguments=()->System.out.println("HelloWorld!");noArguments.run();一个参数:UnaryOperator<Boolean>oneArgument=x->!x;Sys 查看详情

读java8函数式编程笔记06_lambda表达式编写并发程序

1. 阻塞式I/O1.1. 一种通用且易于理解的方式,因为和程序用户的交互通常符合这样一种顺序执行的方式1.2. 将系统扩展至支持大量用户时,需要和服务器建立大量TCP连接,因此扩展性不是很好2. 非阻塞式I/O2.1. ... 查看详情

java函数式编程:lambda表达式

您好,我是湘王,这是我的51CTO博客,欢迎您来,欢迎您再来~Java在其技术发展周期中经历过几次比较重要的变化,如果不是这几次比较重要的变化,恐怕不会有现在这样的江湖地位。个人看来,至少有两次,一是2010~2011年兴... 查看详情

0048java8的新特性

java8的新特性主要是Lambda表达式和流式编程,前提都是需要一个函数式接口。---------------------函数式接口------------------1、函数式接口的定义函数式接口在java中是指有且只有一个抽象方法的接口。java中函数式编程的体现就是Lambda... 查看详情

java8新特性-lambda

1.什么是lambda表达式(1)lambda表达式也被称为箭头函数,匿名函数,闭包。(2)lambda表达式体现的是轻量级函数式编程思想。(3)->符号式lambda表达式核心操作符号,符号左侧是操作函数,右侧是操作表达式。(4)jdk1.8新特性 2.ModelC... 查看详情

java8的函数式编程怎么样?

...码提供任何方便,可变性的魔咒也始终存在。Java8的Lambda表达式没有神奇地转变成函数式语言。相反,它的作用仍是创建强制的强命名类型语言,但有更好的语法支持Lambda表达式函数文本。与此同时,Collection类库也得到了增强,... 查看详情

java8新特性之lambda(代码片段)

...8-the-missing-tutorialJava8中最重要的特性之一就是引入了lambda表达式。这能够使你的代码更加简练,并允许你将行为传递到各处。一段时间以来,Java因为自身的冗长和缺少函数式编程的能力而受到批评。随着函数式编程变得... 查看详情