> 文章列表 > Java函数式编程

Java函数式编程

Java函数式编程

java.util.function包

在使用lambda表达式的时候,经常需要定义一些接口用来辅助编码,这样就会使得本应轻量级的lambda表达式又变得重量级。那是否存在解决方案呢?其实Java 8本身已经提供了一些常见的函数式接口,就在java.util.function包下面。

参数 描述
T 函数输入的第一个参数的类型
U 函数输入的第二个参数的类型
R 函数的结果类型

接口 返回值 描述
Predicate<T> boolean 接受参数,判断是否符合要求
Consumer<T> 接受一个输入参数,并且不返回任何结果
Function<T,R> R 接受一个输入参数,返回一个结果
Supplier<T> T 用来提供一个T对象
UnaryOperator<T> T
BiPredicate<T, U> boolean 接受两个参数,判断是否符合要求
BiConsumer<T, U> 接受两个输入参数的操作,并且不返回任何结果
BiFunction<T, U, R> R 接受两个输入参数,并且返回一个结果

此处列出最基本的几个接口,其他的都是在这些的基础上做了一些简单的封装

例如:IntFunction就是对Function<T,R>的封装。上面的这些函数式接口已经可以处理绝大多数场景了,如果有更复杂的情况,那就得自定义接口。

(1)Predicate:判断参数是否符合要求

Predicate<Integer> predicate = x -> x > 0;
Predicate<Person> predicate = x -> x.name.equals("lisi");
System.out.println(predicate.test(100));
predicate = predicate.and(x -> x % 2 == 0 );
predicate = predicate.or(x -> x.age > 25);
方法 描述
test(T t) 用来处理参数T是否满足要求
and(Predicate<? super T> other) 对当前判断进行“&&”操作。调用当前Predicate的test方法之后再去调用other的test方法,相当于进行两次判断
negate() 对当前判断进行“!”操作,即取非操作,可理解为:!条件
or(Predicate<? super T> other) 对当前判断进行“||”操作,即取或操作,可以理解为 test() || other.test()
isEqual(Object targetRef) 对当前操作进行“=”操作,即取等操作,可以理解为 A == B
package java.util.function;
import java.util.Objects;// Predicate接口主要用来判断一个参数是否符合要求,返回boolean值
@FunctionalInterface
public interface Predicate<T> {/*** 用来处理参数T是否满足要求* @param t t输入参数* @return ture满足条件,false不满足*/boolean test(T t);/*** 可理解为:条件A && 条件B* 调用当前Predicate的test方法之后再去调用other的test方法,相当于进行两次判断*/default Predicate<T> and(Predicate<? super T> other) {Objects.requireNonNull(other);return (t) -> test(t) && other.test(t);}/*** 对当前判断进行“!”操作,即取非操作,可理解为:!条件*/default Predicate<T> negate() {return (t) -> !test(t);}/*** 对当前判断进行“||”操作,即取或操作,可以理解为 test() || other.test()*/default Predicate<T> or(Predicate<? super T> other) {Objects.requireNonNull(other);return (t) -> test(t) || other.test(t);}/*** 对当前操作进行“=”操作,即取等操作,可以理解为 A == B*/static <T> Predicate<T> isEqual(Object targetRef) {return (null == targetRef)? Objects::isNull: object -> targetRef.equals(object);}
}

基本使用
(1)test(T t):判断给定的值是否大于0

Predicate<Integer> predicate = x -> x > 0;
// 结果为:true
System.out.println(predicate.test(100));

(2)and(Predicate<? super T> other):判断给定的值是否是大于100的偶数

Predicate<Integer> predicate = x -> x > 100;
predicate = predicate.and(x -> x % 2 == 0 );
System.out.println(predicate.test(98));  // false
System.out.println(predicate.test(102));  // true
System.out.println(predicate.test(103));  // false

(3)negate():计算一批用户中年龄大于22岁的用户的数量

class Person{private int age;private String name;public Person(int age, String name) {this.age = age;this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}Predicate<Person> personPredicate = x -> x.age > 22;
System.out.println(Stream.of(new Person(21, "zhangsan"),new Person(22, "lisi"),new Person(23, "wangwu"),new Person(24, "wangwu"),new Person(25, "lisi"),new Person(26, "zhangsan")).filter(personPredicate.negate()).count() // 4
);

(4)or(Predicate<? super T> other):计算一批用户中名称为"lisi"或者年龄大于25岁的用户的数量

Predicate<Person> predicate = x -> x.name.equals("lisi");
predicate = predicate.or(x -> x.age > 25);
System.out.println(Stream.of(new Person(21,"zhangsan"),new Person(22,"lisi"),new Person(23,"wangwu"),new Person(24,"wangwu"),new Person(25,"lisi"),new Person(26,"zhangsan")).filter(predicate).count()
);

(5)isEqual(Object targetRef):查找下给定的一批用户中一样的用户

class Person {private int age;private String name;public Person(int age, String name) {this.age = age;this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic boolean equals(Object o) {if (this == o) {return true;}if (o == null || getClass() != o.getClass()) {return false;}Person person = (Person) o;if (age != person.age || !name.equals(person.name)) {return false;}return true;}@Overridepublic int hashCode() {int result = age;result = 31 * result + name.hashCode();return result;}
}Person person = new Person(22, "lisi");
Predicate<Person> predicate = Predicate.isEqual(person);
System.out.println(Stream.of(new Person(21, "zhangsan"),new Person(22, "lisi"),new Person(23, "wangwu"),new Person(24, "wangwu"),new Person(22, "lisi"),new Person(26, "zhangsan")).filter(predicate).count() // 2
);

与Predicate<T>相关接口

接口 描述
BiPredicate<T, U> 针对两个参数,看两个参数是否符合某个条件表达式
DoublePredicate 看一个double类型的值是否符合某个条件表达式
IntPredicate 看一个int类型的值是否符合某个条件表达式
LongPredicate 看一个long类型的值是否符合某个条件表达式

(2)Consumer:接受单个输入参数,并且不返回结果

方法 描述
accept(T t) 对给定的参数执行此操作
andThen(Consumer<? super T> after) 返回一个组合的Consumer,按顺序执行该操作,然后执行after操作
Consumer<Person> consumer = x -> {if (x.getName().equals("lisi")) {list.add(x);}
};
@FunctionalInterface
public interface Consumer<T> {/*** 对给定的参数执行此操作** @param t 输入的参数*/void accept(T t);/*** 返回一个组合的Consumer ,按顺序执行该操作,然后执行after操作* @param after 此操作后执行的操作* @return 一个组合的Consumer按顺序执行该操作,后跟after操作* @throws NullPointerException*/default Consumer<T> andThen(Consumer<? super T> after) {Objects.requireNonNull(after);return (T t) -> { accept(t); after.accept(t); };}
}

将给定的一批用户里面的名称为"lisi"且年龄大于22岁的用户都给打包起来

List<Person> list = new ArrayList<>();
Consumer<Person> consumer = x -> {if (x.getName().equals("lisi")) {list.add(x);}
};consumer = consumer.andThen(
x -> list.removeIf(y -> y.getAge() < 23)
);Stream.of(new Person(21, "zhangsan"),new Person(22, "lisi"),new Person(23, "wangwu"),new Person(24, "wangwu"),new Person(23, "lisi"),new Person(26, "lisi"),new Person(26, "zhangsan")
).forEach(consumer);System.out.println(JSON.toJSONString(lisiList));

与Consumer相关的接口

接口 描述
BiConsumer<T, U> 处理一个两个参数
DoubleConsumer 处理一个double类型的参数
IntConsumer 处理一个int类型的参数
LongConsumer 处理一个long类型的参数
ObjIntConsumer 处理两个参数,且第二个参数必须为int类型
ObjLongConsumer 处理两个参数,且第二个参数必须为long类型

(3)Function<T,R>:接受一个输入参数,返回一个结果的函数

public static void main(String[] args) {Function<Integer, Integer> f = x -> x + 1;System.out.println(f.apply(1));BiFunction<Integer, Integer, Integer> g = (x, y) -> x + y;
System.out.println(g.apply(1, 2));
}
方法 描述
apply(T t) 将此函数应用于给定参数T,真正执行函数接口的方法,并返回R类型
compose(Function<? super V, ? extends T> before) 函数链,before执行的结果作为根函数apply()参数
andThen(Function<? super R, ? extends V> after) 函数链,根函数apply()执行结果做为after的参数
identity() 返回其输入参数的函数
@FunctionalInterface
public interface Function<T, R> {/*** 将此函数应用于给定参数,真正执行函数接口的方法* @param t 函数参数* @return 函数结果*/R apply(T t);/*** 函数链,before执行的结果做根函数为参数*/default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {Objects.requireNonNull(before);return (V v) -> apply(before.apply(v));}/*** 函数链,根函数apply()执行结果做为after的参数*/default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {Objects.requireNonNull(after);return (T t) -> after.apply(apply(t));}/*** 返回其输入参数的函数*/static <T> Function<T, T> identity() {return t -> t;}
}

使用示例

//例1
public class FunctionDemo {public static void main(String[] args) {Function<Integer,Integer> root = (v) -> {System.out.println("root apply");return v + 1;// 12};Function<Integer,Integer> before = (v) -> {System.out.println("before apply");return v + 1;// 先得结果11,再传给root};System.out.println(root.compose(before).apply(10));// before apply// root apply// 12}
}// 例2
public class FunctionDemo {public static void main(String[] args) {Function<Integer,Integer> root = (v) -> {System.out.println("root apply");return v + 1;  // 先得结果11,再传给after};Function<Integer,Integer> after = (v) -> {System.out.println("after apply");return v + 1;// 12};System.out.println(root.andThen(after).apply(10));// root apply// after apply// 12}
}

Function与Consumer接口都有andThen()方法,两者的区别是对参数的使用方式不同。每Consumer使用的是同一个原始参数,Function的参数只会被根函数使用一次,之后的函数使用的是前一个函数的结果做为参数。

接口 描述
BiFunction<T,U,R> 代表了一个接受两个输入参数的方法,并且返回一个结果
DoubleFunction<R> 代表接受一个double值参数的方法,并且返回结果
DoubleToIntFunction 接受一个double类型输入,返回一个int类型结果
DoubleToLongFunction 接受一个double类型输入,返回一个long类型结果
IntFunction<R> 接受一个int类型输入参数,返回一个结果
IntToDoubleFunction 接受一个int类型输入,返回一个double类型结果
IntToLongFunction 接受一个int类型输入,返回一个long类型结果
LongFunction<R> 接受一个long类型输入参数,返回一个结果
LongToDoubleFunction 接受一个long类型输入,返回一个double类型结果
LongToIntFunction 接受一个long类型输入,返回一个int类型结果
ToDoubleBiFunction<T,U> 接受两个输入参数,返回一个double类型结果
ToDoubleFunction<T> 接受一个输入参数,返回一个double类型结果
ToIntBiFunction<T,U> 接受两个输入参数,返回一个int类型结果
ToIntFunction<T> 接受一个输入参数,返回一个int类型结果
ToLongBiFunction<T,U> 接受两个输入参数,返回一个long类型结果
ToLongFunction<T> 接受一个输入参数,返回一个long类型结果

(4)Supplier:提供一个对象

Supplier<T>接口是一个预定义的接口,表示结果的提供者。使用lambda表达式、方法引用或默认构造函数实例化的。Supplier接口的函数方法是get()方法。此接口属于java.util.function函数接口

Supplier是用来提供一个对象,提供的对象的构造则由其来定义。每次调用get()方法时都会调用构造方法创建一个新对象

// 创建Supplier容器,声明为TestSupplier类型,此时并不会调用对象的构造方法,即不会创建对象
Supplier<TestSupplier> sup= TestSupplier::new;
System.out.println("--------");
// 调用get()方法,此时会调用对象的构造方法,即获得到真正对象
sup.get();
// 每次get都会调用构造方法,即获取的对象不同
sup.get();

Java 8中可以通过“::”关键字来访问类的构造方法、对象方法、静态方法

@FunctionalInterface
public interface Supplier<T> {/*** 获取提供的对象实例*/T get();
}

与Supplier 相关的接口

接口 描述
BooleanSupplier 提供一个boolean类型的对象
DoubleSupplier 提供一个double类型的对象
IntSupplier 提供一个int类型的对象
LongSupplier 提供一个long类型的对象

(5)UnaryOperator:对单个操作数产生与其操作数相同类型的结果

表示对单个操作数产生与其操作数相同类型的结果的操作。对于操作数和结果是相同类型的情况,这是Function的Function

UnaryOperator接口的功能是获取一个对象,对其执行某些操作,然后返回相同类型的对象。 这就是函数的一元性质。 一种对象类型进入,而完全相同的类型消失。

@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {/*** 返回始终返回其输入参数的一元运算符* @param <T> 运算符的输入和输出类型* @return 始终返回其输入参数的一元运算符*/static <T> UnaryOperator<T> identity() {return t -> t;}
}

UnaryOperator Lambda表达式
如果使用完整的Java类实现UnaryOperator接口,它将创建完全有效的代码,但无法实现使用功能接口的目的。 函数式编程的全部思想是编写使用非常稀疏和简洁的lambda表达式的代码。使用lambda表达式,可以完全消除对UnaryOperatorExample类的需要,并这样重写整个应用程序:

UnaryOperator<String> extensionAdder = (String text) -> { return text + ".txt";} ;
String newText = extensionAdder.apply("example-function");
System.out.println(newText);

例如:从String中去除所有非数字字符。在这种情况下,将包含一串数字和字母的字符串放入UnaryOperator,然后返回仅包含数字的字符串。 一个字符串进入,一个字符串出来。 那是一个运行中的UnaryOperator。

Stream(并行流)

Stream API是Java 8对集合类的补充与增强。主要用来对集合进行各种便利的聚合操作或者批量数据操作

在Stream API中,流的操作有两种:Intermediate和Terminal
(1)Intermediate
一个流可以后面跟随零个或多个intermediate操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历
Intermediate操作:map(mapToInt,flatMap等)、filter、distinct、sorted、peek、limit、skip、parallel、sequential、unordered

(2)Terminal
一个流只能有一个terminal操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个side effect。
Terminal操作:forEach、forEachOrdered、toArray、reduce、collect、min、max、count、anyMatch、 allMatch、noneMatch、findFirst、findAny、iterator

(3)Short-circuiting
对于一个 intermediate 操作,如果它接受的是一个无限大(infinite/unbounded)的 Stream,但返回一个有限的新Stream。对于一个terminal操作,如果它接受的是一个无限大的Stream,但能在有限的时间计算出结果
Short-circuiting操作:anyMatch、allMatch、noneMatch、findFirst、findAny、limit

Stream特点

(1)没有存储
流不是存储元素的数据结构;相反,它通过计算操作的流水线传送诸如数据结构,阵列,生成器功能或I / O通道的源的元件

(2)功能性质
流上的操作产生结果,但不会修改其来源。 例如,过滤从Stream获得的Stream会生成新的Stream而不需要过滤的元素,而不是从源集合中删除元素

(3)懒惰寻求
许多流操作(如过滤,映射或重复删除)可以懒惰地实现,从而暴露优化的机会。
例如:“找到具有三个连续元音的第一个String”不需要检查所有的输入字符串。流操作分为中间(Stream生产)操作和终端(价值或副作用生成)操作。中级操作总是懒惰

(4)可能无限
虽然集合的大小有限,但流不需要。诸如limit(n)或findFirst()之类的limit(n) findFirst()可以允许无限流上的计算在有限的时间内完成。
消耗品流的元素只能在流的一生中访问一次。像Iterator一样,必须生成一个新流来重新访问源的相同元素

可以“存储”有限个或无限个元素
一个stream可以转换成另一个stream
参数都是Lambda表达式
不会存储元素
惰性计算:在需要获取结果时才进行计算

创建流

// 从集合类创建流。集合对象可以直接通过调用其stream()方法得到对应的流
List<String> list = Arrays.asList("hello", "world", "la");
list.stream();
// 利用数组创建流
String[] strArray = new String[]{"hello", "world", "la"};
Stream.of(strArray);
// 利用可变参数创建流
Stream.of("hello", "world", "la");
// 根据范围创建数值流
IntStream.range(0, 100);         // 不包含最后一个数
IntStream.rangeClosed(0, 99);    // 包含最后一个数

常见的流操作

1.forEach

forEach可以说是最常见的操作了,甚至对于List等实现了Collection接口的类可以不创建stream而直接使用forEach。简单地说,forEach就是遍历并执行某个操作。

Stream.of("a", "b", "c", "d").forEach(System.out::println);

2.Map

map也同样是一个非常高频的流操作,用来将一个集合映射为另一个集合

// 将[1,2,3,4]映射为[1,4,9,16]
IntStream.rangeClosed(1, 4).map(x -> x * x).forEach(System.out::println);

除此之外,还有一个叫做flatMap的操作,这个操作在映射的基础上又做了一层扁平化处理。这个概念可能比较难理解,那举个例子,我们需要将[“hello”, “world”]转换成[h,e,l,l,o,w,o,r,l,d],可以尝试一下使用map,那你会惊讶地发现,可能结果不是你想象中的那样。如果不信可以执行下面这段代码,就会发现map与flatMap之间的区别了

Stream.of("hello", "world").map(s -> s.split("")).forEach(System.out::println);
System.out.println("--------------");
Stream.of("hello", "world").flatMap(s -> Stream.of(s.split(""))).forEach(System.out::println);

3.filter

filter则实现了过滤的功能,如果只需要[1,2,3,4,5]中的奇数,可以如下

Stream.of("hello", "world").map(s -> s.split("")).forEach(System.out::println);
System.out.println("--------------");
Stream.of("hello", "world").flatMap(s -> Stream.of(s.split(""))).forEach(System.out::println);

4.sorted和distinct

其中sorted表示排序,distinct表示去重,简单的示例如下:

// 千万不要用int
Integer[] arr = new Integer[]{5, 1, 2, 1, 3, 1, 2, 4};
Stream.of(arr).sorted().forEach(System.out::println);
Stream.of(arr).distinct().forEach(System.out::println);
Stream.of(arr).distinct().sorted().forEach(System.out::println);

5.collect

在流操作中往往是从一个List得到另一个List,而不是直接通过forEach来打印。那么这个时候就需要使用到collect了。依然是之前的例子,将[1,2,3,4]转换成[1,4,9,16]。

List<Integer> list1= Stream.of(1, 2, 3, 4).map(x -> x * x).collect(Collectors.toList());
// 对于IntStream生成的流需要使用mapToObj而不是map
List<Integer> list2 = IntStream.rangeClosed(1, 4).mapToObj(x -> x * x).collect(Collectors.toList());

Optional

Optional的引入是为了解决空指针异常的问题,事实上在Java8之Optional的基本操作

创建Optional

Optional.empty();          // 创建一个空Optional
Optional.of(T value);      // 不接受null,会报NullPointerException异常
Optional.ofNullable(T value);     // 可以接受null

获取结果

get();                                   // 返回里面的值,如果值为null,则抛异常
orElse(T other);                         // 有值则返回值,null则返回other
orElseGet(Supplier other);               // 有值则返回值,null则由提供的lambda表达式生成值
orElseThrow(Supplier exceptionSupplier); // 有值则返回值,null则抛出异常

判断是否为空

isPresent();       // 判断是否为空

链式方法

在Optional中也有类似于Stream API中的链式方法map、flatMap、filter、ifPresent。这些方法才是Optional的精髓。此处以最典型的map作为例子,可以看看map的源码

public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {Objects.requireNonNull(mapper);if (!isPresent())return empty();else {return Optional.ofNullable(mapper.apply(value));}
}

源码很简单,可以看到对于null情况仍然返回null,否则返回处理结果

public static void main(String[] args) {Person person = new Person();String result = Optional.ofNullable(person).map(per -> per.address).map(address -> address.country).map(country -> country.name).orElse(null);System.out.println(result);
}

map与flatMap区别

这两者的区别,同样使用一个简单的例子来解释一下吧

public class FunctionMain {public static void main(String[] args) {Person person = new Person();String name = Optional.ofNullable(person).flatMap(p -> p.name).orElse(null);System.out.println(name);}
}class Person {Optional<String> name;
}

在这里使用的不是map而是flatMap,稍微观察一下,可以发现Person中的name不再是String类型,而是Optional类型了,如果使用map的话,那map的结果就是Optional了,很显然不是我们想要的,flatMap就是用来将最终的结果扁平化(简单地描述,就是消除嵌套)的。至于filter和ifPresent用法类似,就不再叙述了。


上一篇:Java 1.8新特性