用JDK8的函数式接口实现map和reduce操作
2018-12-15 本文已影响0人
m2fox
JDK8引入了很多函数式编程相关的新特性,比如Lambda表达式,为了保持兼容性,Java官方并没有像其他函数式编程语言那样引入一种新的数据结构类型来表示Lambda表达式,比如其他语言中的函数对象,而是引入了一个称为"函数式接口"的概念来表示Lambda表达式,虽然没有让Java变成一个"纯"函数式编程语言,但也大大扩展了Java的函数式编程能力。
什么是函数式接口
简单来说,一个函数式接口首先也是一个用interface
关键词定义的普通接口,其次这个接口有且只有一个抽象方法,不过可以有多个默认方法和静态方法(这两个概念也是JDK8引入的),并且提倡用@FunctionalInterface
注解来修饰函数式接口(但这一点并不是强制要求的)。一个函数式接口就可以代表一类相似操作的Lambda表达式。
实现map操作
用Python的map函数来解释map操作
在Python中有一个内置的map函数,其函数文档如下:
map(...)
map(function, sequence[, sequence, ...]) -> list
Return a list of the results of applying the function to the items of
the argument sequence(s). If more than one sequence is given, the
function is called with an argument list consisting of the corresponding
item of each sequence, substituting None for missing values when not all
sequences have the same length. If the function is None, return a list of
the items of the sequence (or a list of tuples if more than one sequence).
可以看到,map函数接收至少两个参数,第一个参数是一个函数(或Lambda表达式),这个函数接收一个入参(一元函数),第二个参数是一个序列。而返回值是一个列表,这个列表是用传入的函数分别作用于传入的序列的每个元素之后得到的一个新列表。
举一个例子,比如下面的代码就实现了这样一个目的:把一个数列的每一个数都扩大10倍:
In [2]: nums = [1,2,3,4,5]
In [3]: results = map(lambda x:10*x,nums)
In [4]: print results
[10, 20, 30, 40, 50]
用Java函数式接口实现map操作
直接上代码:
- MyMap.java:
package com.function.functionalinterface;
import java.util.ArrayList;
import java.util.List;
/**
* 实现map操作的函数式接口
*
* @param <T>
* @param <R>
*/
@FunctionalInterface
public interface MyMap<T, R> {
/**
* 匿名函数方法签名定义
*
* @param t
* @return
*/
R operate(T t);
/**
* 实现map功能的静态方法
*
* @param function
* 匿名函数
* @param sequence
* 待操作序列
* @return
*/
public static <V, E> List<E> map(MyMap<V, E> function, List<V> sequence) {
// 如果函数或待操作序列为空,则返回null
if (null == function || null == sequence || sequence.isEmpty()) {
return null;
}
List<E> results = new ArrayList<E>();
sequence.forEach(e -> results.add(function.operate(e)));
return results;
}
}
- MyMapTest.java:
package com.function.functionalinterface;
import java.util.ArrayList;
import java.util.List;
public class MyMapTest {
public static void main(String[] args) {
// 构造一个整型列表
List<Integer> nums = new ArrayList<Integer>();
for (int i = 1; i <= 10; i++) {
nums.add(i);
}
// 将整型列表每个元素先乘10,再转化为字符串,并拼接上一个逗号,最终输出得到的字符串列表
System.out
.println(MyMap.map((e) -> String.valueOf(e * 10) + ";", nums));
}
}
- 运行
MyMapTest.java
,输出:
[10;, 20;, 30;, 40;, 50;, 60;, 70;, 80;, 90;, 100;]
实现reduce操作
用Python的reduce函数来解释map操作
在Python中还有个内置函数叫reduce,其说明文档如下:
reduce(...)
reduce(function, sequence[, initial]) -> value
Apply a function of two arguments cumulatively to the items of a sequence,
from left to right, so as to reduce the sequence to a single value.
For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates
((((1+2)+3)+4)+5). If initial is present, it is placed before the items
of the sequence in the calculation, and serves as a default when the
sequence is empty.
文档已经说的很明确了,话不多说,举一个例子来感受一下reduce函数的作用:
In [14]: nums = [1,2,3,4,5]
In [15]: results = reduce(lambda x,y:x+y,nums)
In [16]: print results
15
In [17]: results = reduce(lambda x,y:x+y,nums,1000)
In [18]: print results
1015
用Java函数式接口实现reduce操作
直接上代码:
- MyReduce.java:
package com.function.functionalinterface;
import java.util.List;
/**
* 实现reduce功能的函数式接口
*
* @param <T>
*/
@FunctionalInterface
public interface MyReduce<T> {
/**
* 匿名函数方法签名定义
* @param t1
* @param t2
* @return
*/
T operate(T t1, T t2);
/**
* 实现reduce功能的静态方法
* @param function 匿名函数
* @param sequence 待操作序列
* @return
*/
public static <E> E reduce(MyReduce<E> function, List<E> sequence) {
// 如果传入的函数为空,或序列为空,则返回null
if (null == function || null == sequence || 0 == sequence.size()) {
return null;
}
// 如果传入的序列只有一个元素,则返回这个元素
if (1 == sequence.size()) {
return sequence.get(0);
}
// 如果传入的序列至少有两个元素,则返回对所有元素从前到后依次进行两两操作的结果
E result = sequence.get(0);
for (int i = 1; i < sequence.size(); i++) {
result = function.operate(result, sequence.get(i));
}
return result;
}
}
- MyReduceTest.java:
package com.function.functionalinterface;
import java.util.ArrayList;
import java.util.List;
public class MyReduceTest {
public static void main(String[] argv) {
// 构造一个整型列表和一个字符串列表
List<Integer> nums = new ArrayList<Integer>();
List<String> strs = new ArrayList<String>();
for (int i = 1; i <= 10; i++) {
nums.add(i);
strs.add(i + ";");
}
// 输出整型列表所有元素求和的结果
System.out.println(MyReduce.reduce((n1, n2) -> n1 + n2, nums));
// 输出拼接字符串列表每个元素的结果
System.out.println(MyReduce.reduce((s1, s2) -> s1 + s2, strs));
}
}
- 运行
MyReduceTest.java
,输出如下:
55
1;2;3;4;5;6;7;8;9;10;
总结
本文用函数式接口实现了非常常见的两种操作:map和reduce,也可以体会到在JDK8中用函数式接口进行编程的基本用法。更多Java函数式接口及Lambda表达式的用法,将在后续文章中探讨。