- /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
- * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
- */
- package java.util.function;
-
- import java.util.Objects;
-
- /**
- * Represents a function that accepts one argument and produces a result.
- * 表示接受一个参数并产生一个结果的函数。
- *
- * @param
the type of the input to the function - * 参数
是函数的输入类型 - * @param
the type of the result of the function - * 参数
是函数的返回类型 - * @since 1.8
- */
- // 注明是函数式接口
- @FunctionalInterface
- public interface Function
{ -
- /**
- * Applies this function to the given argument.
- * 将此函数应用于给定参数。
- * @param t 函数参数
- * @return R 函数返回类型
- */
- R apply(T t);
-
- /**
- * Returns a composed function that first applies the {@code before}
- * function to its input, and then applies this function to the result.
- * If evaluation of either function throws an exception, it is relayed to
- * the caller of the composed function.
- *
- * @param
the type of input to the {@code before} function, and to the - * composed function
- * @param before the function to apply before this function is applied
- * @return a composed function that first applies the {@code before}
- * function and then applies this function
- * @throws NullPointerException if before is null
- *
- * @see #andThen(Function)
- */
- default
Function compose(Function super V, ? extends T> before) { - Objects.requireNonNull(before);
- return (V v) -> apply(before.apply(v));
- }
-
- /**
- * Returns a composed function that first applies this function to
- * its input, and then applies the {@code after} function to the result.
- * If evaluation of either function throws an exception, it is relayed to
- * the caller of the composed function.
- * 返回一个组合函数,该函数首先将该函数应用于其输入,然后将该函数应用于结果。如果对任一函数的求值抛出异常,则将其中继到组合函数的调用方。
- * @param
the type of output of the {@code after} function, and of the - * composed function
- * @param after the function to apply after this function is applied
- * @return a composed function that first applies this function and then
- * applies the {@code after} function
- * @throws NullPointerException if after is null
- *
- * @see #compose(Function)
- */
- default
Function andThen(Function super R, ? extends V> after) { - Objects.requireNonNull(after);
- return (T t) -> after.apply(apply(t));
- }
-
- /**
- * Returns a function that always returns its input argument.
- * 该方法返回一个函数,该函数返回输入的参数。
- * @param
the type of the input and output objects to the function - * @return a function that always returns its input argument
- */
- static
Function identity() { - return t -> t;
- }
- }
之前只了解Function是函数式接口,支持Lambda表达式,今日由于需要,才了解到Function
的identity()
。
Function
的identity()
返回t -> T
,即本身。
也就是说identity()
可以换为t -> T
今天的需求大概是:
- 已经有了List
,利用stream()获取一个键值对Map。 -
- // 构造Map键值对,key:Integer, value:IndexEntity
- // key为指标实体的id,value为对应的指标实体
- Map
map = indexEntities.stream().collect(Collectors.toMap(IndexEntity::getId, Function.identity()));
IndexEntity::getId
是Java8新出的方法引用。
- // 将Stream转换成容器或Map
-
- Stream
stream = Stream.of("I", "love", "you", "too"); -
- Map
map = stream.collect(Collectors.toMap(Function.identity(), String::length));
Function是一个接口,那么Function.identity()是什么意思呢?解释如下:
Java 8允许在接口中加入具体方法。接口中的具体方法有两种,default方法和static方法,identity()就是Function接口的一个静态方法。
Function.identity()返回一个输出跟输入一样的Lambda表达式对象,等价于形如t -> t
形式的Lambda表达式。
identity() 方法JDK源码如下:
- static Function identity() {
-
- return t -> t;
-
- }
下面的代码中,Task::getTitle
需要一个task并产生一个仅有一个标题的key。task -> task
是一个用来返回自己的lambda表达式,上例中返回一个task。
- private static Map
taskMap(List tasks) { -
- return tasks.stream().collect(toMap(Task::getTitle, task -> task));
-
- }
可以使用Function
接口中的默认方法identity
来让上面的代码代码变得更简洁明了、传递开发者意图时更加直接,下面是采用identity
函数的代码。
- import static java.util.function.Function.identity;
-
-
-
- private static Map
taskMap(List tasks) { -
- return tasks.stream().collect(toMap(Task::getTitle, identity()));
-
- }
- Arrays.asList("a", "b", "c")
-
- .stream()
-
- .map(Function.identity()) // <- This,
-
- .map(str -> str) // <- is the same as this.
-
- .collect(Collectors.toMap(
-
- Function.identity(), // <-- And this,
-
- str -> str)); // <-- is the same as this.
上面的代码中,为什么要使用Function.identity()
代替str->str
呢?它们有什么区别呢?
在上面的代码中str -> str
和Function.identity()
是没什么区别的因为它们都是t->t
。但是我们有时候不能使用Function.identity
,看下面的例子:
- List list = new ArrayList<>();
-
- list.add(1);
-
- list.add(2);
下面这段代码可以运行成功:
int[] arrayOK = list.stream().mapToInt(i -> i).toArray();
但是如果你像下面这样写:
int[] arrayProblem = list.stream().mapToInt(Function.identity()).toArray();
运行的时候就会错误,因为mapToInt
要求的参数是ToIntFunction
类型,但是ToIntFunction
类型和Function
没有关系
Lambda表达式也称为闭包,它允许我们把函数当作参数一样传递给某个方法,或者把代码本身当作数据处理。
早期Java开发者只能使用匿名内部类来实现Lambda表达式。
最简单的可以由逗号分隔的参数列表、->
符号、语句块三部分组成。
例如:
- // 例子1
- // 参数e的类型是编译器推理出来的
- Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) );
-
- // 例子2
- // 当然也可以将执行参数的类型写上
- Arrays.asList( "a", "b", "d" ).forEach((String e)-> System.out.println( e ) );
-
- // 例子3
- // 当有多个参数时
- Arrays.asList( "a", "b", "d" ).sort((e1,e2)-> e1.compareTo(e2));
-
- // 例子4
- // 当Lambda的语句块只有一行时,可以不使用return语句。
- Arrays.asList( "a", "b", "d" ).sort((e1,e2)-> e1.compareTo(e2));
ps: 切记当有多个参数,或需要指定参数类型的时候,参数列表要加括号。
函数式接口(Functional Interface
)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
作用: 这样的接口可以隐式转换为Lambda表达式。
只要某个开发者在该接口中添加一个函数,则该接口就不再是函数式接口,进而导致编译失败。为了客服这种问题,并显式说明某个接口是函数式接口,Java8提供了一个特殊的注解**@FunctionalInterface**Java 库中的所有相关接口都已经带有这个注解了。
- @FunctionalInterface
- interface Addtions {
- int test(int a, int b);// 我是核心
- default void hello() {
- System.out.println("我不会影响到函数式接口的定义");
- }
- static void hello1(){
- System.out.println("我也不会影响到函数式接口的定义");
- }
- }
常用的几个接口:
R apply(T t);
T get();
boolean test(T t);
void accept(T t);
写lamdba表达式时会经常用到四个标黑的函数式接口,重点是她们方法的返回值和方法参数。
Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default
关键字即可,这个特征又叫做扩展方法,示例如下:
- class FunctionalInterfaceTest implements Formula{
- @Override
- public double calculate(int a) {
- return 0;
- }
- // 可以重写sqrt方法。
- @Override
- public double sqrt(int a) {
- return Formula.super.sqrt(a);
- }
- }
- @FunctionalInterface
- interface Formula {
- double calculate(int a);
-
- // 该方法(默认方法)可以被实现类重写
- default double sqrt(int a) {
- return Math.sqrt(a);
- }
- static void hello1(){
- System.out.println("我是新来的(JAVA8),我叫静态方法,");
- }
- }
方法引用使得开发者可以直接引用现存的方法、Java类的构造方法或者实例对象。方法引用和Lambda表达式配合使用,使得java类的构造方法看起来紧凑而简洁,没有很多复杂的模板代码。
可见使用Lambda表达式的写法和使用方法引用的写法的效果是一样的,但是使用方法引用有时会更加简化代码
类名::new
类名::静态方法
类名::方法
实例对象::成员方法
- public class Test {
- private String name;
- public String getName() {
- return this.name;
- }
- public Test(String name) {
- this.name = name;
- }
- public static String staticMethod(){
- return "我是静态方法!";
- }
-
- public static void main(String[] args) {
- Test test1 = new Test("小明");
-
- // Lambda表达式
- Supplier
func1 = () -> test1.getName(); - System.out.println("Lambda表达式测试:" + func1.get());
-
- // 实例方法引用
- Supplier
func2 = test1::getName; - System.out.println("方法引用方式测试:" + func2.get());
-
- // 静态方法引用
- Supplier
func3 = Test::staticMethod; - System.out.println("静态方法引用测试:" + func3.get());
-
- // 构造方法引用(构造器引用)
- Function
func4 = Test::new; - Test test2 = func4.apply("xxx");
- System.out.println("构造方法引用测试:" + test2);
-
- // 对象方法引用
- // Test为类名,getName为成员方法。
- Function
func5 = Test::getName; - System.out.println("对象方法测试引用:" + func5.apply(test1));
- }
- }
Java应用中最常见的bug就是NullPointerException,
就比如比较两个字符串是否相等
s1.equals(s2)
,如果s1==null
,那么一运行,console
立马就爆红了。
所以Java8提供了Optional来解决这问题。
If a value is present, and the value matches the given predicate,return an {@code Optional} describing the value, otherwise return an empty {@code Optional}.
- Optional< String > fullName = Optional.ofNullable( null );
- System.out.println( "Full Name is set? " + fullName.isPresent() );
- System.out.println( "Full Name: " + fullName.orElseGet( () -> "[none]" ) );
- System.out.println( fullName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );
-
- // 下面为输出结果
- Full Name is set? false
- Full Name: [none]
- Hey Stranger!
2.当optional实例不为null时
- Optional< String > firstName = Optional.of( "Tom" );
- System.out.println( "First Name is set? " + firstName.isPresent() );
- System.out.println( "First Name: " + firstName.orElseGet( () -> "[none]" ) );
- System.out.println( firstName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ));
-
- //输出结果
- First Name is set? true
- First Name: Tom
- Hey Tom!
转载
Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
Stream(流)是一个来自数据源的元素队列并支持聚合操作
和以前的Collection操作不同, Stream操作还有两个基础的特征:
- public class ParallelStream {
- public static void main(String[] args) {
- List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
- numbers.stream().forEach(System.out::print);
- }
- }
- numbers.stream().forEach(System.out::print);
- numbers.stream().forEach(i->System.out.print(i));
上述两种方法是等价的。
forEach用来对stream中的数据进行迭代,比如上面创建流的操作就使用了forEach。看会上面的例子后理解forEach不会很难的。需要注意的是,forEach操作是不能改变遍历对象本身的。
map 方法用于映射每个元素到对应的结果,多数情况下用来处理数据。下面给出一个让原list个位置元素自增2的代码:
- public class MapDemo {
- public static void main(String[] args) {
- List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
- List<Integer> outPutList = numbers.stream().map(i -> i + 2).distinct().collect(Collectors.toList());
- outPutList.forEach(n->System.out.print(n+" "));
- }
- }
filter 方法用于通过设置的条件过滤出元素。以下代码片段使用 filter 方法过滤出空字符串:
- List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
- // 获取空字符串的数量
- int count = strings.stream().filter(string -> string.isEmpty()).count();
limit 方法用于获取指定数量的流。 以下代码片段使用 limit 方法打印出 10 条数据:
- Random random = new Random();
- random.ints().limit(10).forEach(System.out::println);
还有一个常用的是配合skip()
方法用来进行分页操作。
- int pageSize=10;
- int currentPage=1;
- return pageList.stream()
- .skip(pageSize * (currentPage-1))
- .limit(pageSize)
- .collect(Collectors.toList());
sorted 方法用于对流进行排序。以下代码片段使用 sorted 方法对输出的 10 个随机数进行排序:
- Random random = new Random();
- random.ints().limit(10).sorted().forEach(System.out::println);
Collectors 可用于返回列表或字符串,上面介绍map的例子就用到了Collectors,下面给出菜鸟教程的一个例子:
- List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
- List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
-
- System.out.println("筛选列表: " + filtered);
- String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
- System.out.println("合并字符串: " + mergedString);
顾名思义,统计就是用来统计数据的,一般用于int、double、long等基本类型上。
- List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
- IntSummaryStatistics stats = integers.stream().mapToInt((x) -> x).summaryStatistics();
-
- System.out.println("列表中最大的数 : " + stats.getMax());
- System.out.println("列表中最小的数 : " + stats.getMin());
- System.out.println("所有数之和 : " + stats.getSum());
- System.out.println("平均数 : " + stats.getAverage());
Stream先介绍到这里,我们Stream代码觉得陌生是因为刚接触声明式编程的风格,下一篇应该会介绍lambda表达式和Optional的用法,我们会更多地用声明式的编程风格。
转载
Java8 添加了一个新的特性Function,顾名思义这一定是一个函数式的操作。我们知道Java8的最大特性就是函数式接口。所有标注了@FunctionalInterface
注解的接口都是函数式接口,具体来说,所有标注了该注解的接口都将能用在lambda表达式上。
标注了@FunctionalInterface
的接口有很多,但此篇我们主要讲Function,了解了Function其他的操作也就很容易理解了。
- @FunctionalInterface
- public interface Function<T, R> {
- R apply(T t);
- /**
- * @return a composed function that first applies the {@code before}
- * function and then applies this function
- */
- default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
- Objects.requireNonNull(before);
- return (V v) -> apply(before.apply(v));
- }
- /**
- * @return a composed function that first applies this function and then
- * applies the {@code after} function
- */
- default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
- Objects.requireNonNull(after);
- return (T t) -> after.apply(apply(t));
- }
- }
为了方便地阅读源码,我们需要了解一些泛型的知识,如果你对泛型已经很熟悉了,那你可以跳过这段 。
泛型是JDK1.5引入的特性,通过泛型编程可以使编写的代码被很多不同的类型所共享,这可以很好的提高代码的重用性。因为本篇重点不是介绍泛型,所以我们只关注上述Function源码需要用到的泛型含义。
泛型类使用
来表示该类为泛型类,其内部成员变量和函数的返回值都可以为泛型
,Function源码的标识为
,也就是两个泛型参数,此处不再赘述,具体泛型类可以看网上的文章。
在方法修饰符的后面加一个
表明该方法为泛型方法,如Function 的源码里的compose方法的
。通配符也很好理解,还是compose的例子,我们可以看到compose的参数为一个Function类型,其中Functin的参数指定了其第一个参数必须是V的父类,第二个参数必须继承T,也就是T的子类。
讲完了上面这些就可以开始研究源码了。
首先我们已经知道了Function是一个泛型类,其中定义了两个泛型参数T和R,在Function中,T代表输入参数,R代表返回的结果。也许你很好奇,为什么跟别的java源码不一样,Function 的源码中并没有具体的逻辑呢?
其实这很容易理解,Function 就是一个函数,其作用类似于数学中函数的定义 ,(x,y)跟
y=f(x)y=f(x)
所以Function中没有具体的操作,具体的操作需要我们去为它指定,因此apply具体返回的结果取决于传入的lambda表达式。
R apply(T t);
举个例子:
- public void test(){
- Function<Integer,Integer> test=i->i+1;
- test.apply(5);
- }
- /** print:6*/
我们用lambda表达式定义了一个行为使得i自增1,我们使用参数5执行apply,最后返回6。这跟我们以前看待Java的眼光已经不同了,在函数式编程之前我们定义一组操作首先想到的是定义一个方法,然后指定传入参数,返回我们需要的结果。函数式编程的思想是先不去考虑具体的行为,而是先去考虑参数,具体的方法我们可以后续再设置。
再举个例子:
- public void test(){
- Function<Integer,Integer> test1=i->i+1;
- Function<Integer,Integer> test2=i->i*i;
- System.out.println(calculate(test1,5));
- System.out.println(calculate(test2,5));
- }
- public static Integer calculate(Function<Integer,Integer> test,Integer number){
- return test.apply(number);
- }
- /** print:6*/
- /** print:25*/
我们通过传入不同的Function,实现了在同一个方法中实现不同的操作。在实际开发中这样可以大大减少很多重复的代码,比如我在实际项目中有个新增用户的功能,但是用户分为VIP和普通用户,且有两种不同的新增逻辑。那么此时我们就可以先写两种不同的逻辑。除此之外,这样还让逻辑与数据分离开来,我们可以实现逻辑的复用。
当然实际开发中的逻辑可能很复杂,比如两个方法F1,F2都需要两个个逻辑AB,但是F1需要A->B,F2方法需要B->A。这样的我们用刚才的方法也可以实现,源码如下:
- public void test(){
- Function<Integer,Integer> A=i->i+1;
- Function<Integer,Integer> B=i->i*i;
- System.out.println("F1:"+B.apply(A.apply(5)));
- System.out.println("F2:"+A.apply(B.apply(5)));
- }
- /** F1:36 */
- /** F2:26 */
-
也很简单呢,但是这还不够复杂,假如我们F1,F2需要四个逻辑ABCD,那我们还这样写就会变得很麻烦了。
compose和andThen可以解决我们的问题。先看compose的源码
- default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
- Objects.requireNonNull(before);
- return (V v) -> apply(before.apply(v));
- }
compose接收一个Function参数,返回时先用传入的逻辑执行apply,然后使用当前Function的apply。
- default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
- Objects.requireNonNull(after);
- return (T t) -> after.apply(apply(t));
- }
andThen跟compose正相反,先执行当前的逻辑,再执行传入的逻辑。
这样说可能不够直观,我可以换个说法给你看看
compose等价于B.apply(A.apply(5)),而andThen等价于A.apply(B.apply(5))。
- public void test(){
- Function<Integer,Integer> A=i->i+1;
- Function<Integer,Integer> B=i->i*i;
- System.out.println("F1:"+B.apply(A.apply(5)));
- System.out.println("F1:"+B.compose(A).apply(5));
- System.out.println("F2:"+A.apply(B.apply(5)));
- System.out.println("F2:"+B.andThen(A).apply(5));
- }
- /** F1:36 */
- /** F1:36 */
- /** F2:26 */
- /** F2:26 */
我们可以看到上述两个方法的返回值都是一个Function,这样我们就可以使用建造者模式的操作来使用。
B.compose(A).compose(A).andThen(A).apply(5);
这个操作很简单,你可以自己试试。
转载
上一篇学习了下Function接口的使用,本篇我们学习下另一个实用的函数式接口Predicate。
Predicate的源码跟Function的很像,我们可以对比这两个来分析下。直接上Predicate的源码:
- public interface Predicate<T> {
- /**
- * Evaluates this predicate on the given argument.
- */
- boolean test(T t);
-
- /**
- * Returns a composed predicate that represents a short-circuiting logical
- * AND of this predicate and another. When evaluating the composed
- * predicate, if this predicate is {@code false}, then the {@code other}
- * predicate is not evaluated.
- */
- default Predicate<T> and(Predicate<? super T> other) {
- Objects.requireNonNull(other);
- return (t) -> test(t) && other.test(t);
- }
-
- /**
- * Returns a predicate that represents the logical negation of this
- * predicate.
- */
- default Predicate<T> negate() {
- return (t) -> !test(t);
- }
-
- /**
- * Returns a composed predicate that represents a short-circuiting logical
- * OR of this predicate and another. When evaluating the composed
- * predicate, if this predicate is {@code true}, then the {@code other}
- * predicate is not evaluated.
- */
- default Predicate<T> or(Predicate<? super T> other) {
- Objects.requireNonNull(other);
- return (t) -> test(t) || other.test(t);
- }
-
- /**
- * Returns a predicate that tests if two arguments are equal according
- * to {@link Objects#equals(Object, Object)}.
- */
- static <T> Predicate<T> isEqual(Object targetRef) {
- return (null == targetRef)
- ? Objects::isNull
- : object -> targetRef.equals(object);
- }
- }
Predicate是个断言式接口其参数是
boolean test(T t);
接下来我们看看Predicate默认实现的三个重要方法and,or和negate
- 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);
- }
-
- default Predicate<T> or(Predicate<? super T> other) {
- Objects.requireNonNull(other);
- return (t) -> test(t) || other.test(t);
- }
这三个方法对应了java的三个连接符号&&、||和!,基本的使用十分简单,我们给一个例子看看:
- int[] numbers= {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
- List<Integer> list=new ArrayList<>();
- for(int i:numbers) {
- list.add(i);
- }
- Predicate<Integer> p1=i->i>5;
- Predicate<Integer> p2=i->i<20;
- Predicate<Integer> p3=i->i%2==0;
- List test=list.stream().filter(p1.and(p2).and(p3)).collect(Collectors.toList());
- System.out.println(test.toString());
- /** print:[6, 8, 10, 12, 14]*/
我们定义了三个断言p1,p2,p3。现在有一个从1~15的list,我们需要过滤这个list。上述的filter是过滤出所有大于5小于20,并且是偶数的列表。
假如突然我们的需求变了,我们现在需要过滤出奇数。那么我不可能直接去改Predicate,因为实际项目中这个条件可能在别的地方也要使用。那么此时我只需要更改filter中Predicate的条件。
- List test=list.stream().filter(p1.and(p2).and(p3.negate())).collect(Collectors.toList());
- /** print:[7, 9, 11, 13, 15]*/
我们直接对p3这个条件取反就可以实现了。是不是很简单?
isEqual这个方法的返回类型也是Predicate,所以我们也可以把它作为函数式接口进行使用。我们可以当做==操作符来使用。
- List test=list.stream()
- .filter(p1.and(p2).and(p3.negate()).and(Predicate.isEqual(7)))
- .collect(Collectors.toList());
- /** print:[7] */
-
- java8 函数式接口编程:https://blog.csdn.net/qq_28410283/category_7718494.html
便于检索查找。