Java8 Lambda(一)-函数式接口

实习前只是粗略的看了下Java8的一些基本语法,但是没有系统的学习过.在使用一段时间后决定系统的对其进行一次分析,加深对Java8函数式编程的理解,提高自己的编码技巧.另外kotlin崛起,感兴趣的朋友尝试下混编也未尝不可.


函数式接口

函数式接口,对于Java来说就是接口内只有一个公开方法的接口,因为使用lanbda表达式,例如() -> user.getName()对应的调用则可能是func.get(),编译器会根据接口推断所属于的方法,如果有两个则无法推断.Java8提供了很多函数式接口,一般都使用注解@FunctionalInterface声明,有必要了解如下一些函数式接口.

函数式接口

参数类型

返回类型

描述

Supplier

T

接收一个T类型的值

Consumer

T

处理一个T类型的值

BiConsumer<T, U>

T,U

处理T类型和U类型的值

Predicate

T

boolean

处理T类型的值,并返回true或者false.

ToIntFunction

T

int

处理T类型的值,并返回int值

ToLongFunction

T

long

处理T类型的值,并返回long值

ToDoubleFunction

T

double

处理T类型的值,并返回double值

Function<T, R>

T

R

处理T类型的值,并返回R类型值

BiFunction<T, U, R>

T,U

R

处理T类型和U类型的值,并返回R类型值

BiFunction<T, U, R>

T,U

R

处理T类型和U类型的值,并返回R类型值

UnaryOperator

T

T

处理T类型值,并返回T类型值,

BinaryOperator

T,T

T

处理T类型值,并返回T类型值

以上的函数每一个代表的都是一种基本的操作,操作之间可以自由组合,所以才有了stream这些灵活的操作.

Stream操作

Stream的操作是建立在函数式接口的组合上的,最好的学习方法是看Stream接口来学习.下面举一些例子来分析,假设有这样的一些初始数据.

List<String> testData = new ArrayList<String>();
    testData.add("张三");
    testData.add("李四");
    testData.add("王二");
    testData.add("麻子");

filter

Stream<T> filter(Predicate<? super T> predicate);

filter接收predicate函数,predicate是接收T值,返回boolean值,那么对应的引用就可以写成如下形式,意思是取集合中以’张’开头的名字.

testData.stream()
        .filter(x -> x.startsWith("张"))

map

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

map操作接收的是Function接口,对于Function接收T值返回R值,那map的作用就很明显是转换用的,比如下面代码,转换名称为对应的名称长度,也就是从输入String数据返回int数据.

testData.stream()
        .map(x -> x.length())

flatMap

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

flatMap和map都是使用Function接口,不同的是返回值flatMap限定为Stream类型.所以flatMap可以作为合并流使用,如以下代码,提取出所有的字符.

testData.stream()
        .flatMap(x -> Stream.of(x.split("")))
        .collect(Collectors.toList());
        //输出  [张, 三, 李, 四, 王, 二, 麻, 子]

peek

Stream<T> peek(Consumer<? super T> action);

peek参数为Consumer,Consumer接收T值,无返回,那么该方法就可以作为调试不影响stream中内容的一些操作,不过由于对象都是地址引用,你再此做一些对象内容操作也是可以的. reduce

<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);

Reduce比较复杂的一个接口,属于归纳性操作,看参数,第一个是U泛型,也就是输入类型的参数,最为初始值,第二个BiFunction,接收T,U参数,返回U类型参数,BinaryOperator接收U,U类型,并返回U类型.

 StringBuilder identity = new StringBuilder();
 StringBuilder reduce = testData.stream()
     .flatMap(x -> Stream.of(x.split("")))
     .reduce(identity, (r, x) -> {
       r.append(x);
       return r;
     }, StringBuilder::append);
 System.out.println(identity == reduce);
 System.out.println(reduce.toString());
 //输出 true
//  张三李四王二麻子

首先提供一个基本容器identity,然后两个参数r即是identity,x为每次输入参数,最后一个StringBuilder::append是并发下多个identity的合并策略. 再举个例子,既然reduce属于归纳性操作,那么也可以当成collect使用,如下:

ArrayList<String> identity = new ArrayList<>();
   ArrayList<String> result = testData.stream()
       .flatMap(x -> Stream.of(x.split("")))
       .reduce(identity, (r, x) -> {
         r.add(x);
         return r;
       },(r1,r2) -> {
         r1.addAll(r2);
         return r1;
       });
   System.out.println(identity == result);
   System.out.println(result);
   //输出 true
   //[张, 三, 李, 四, 王, 二, 麻, 子]

强大的collect

collect无疑是stream中最强大的操作,掌握了collect操作才能说掌握了stream.为了便于使用者,Java提供了Collectors类,该类提供了很多便捷的collect操作,如Collector<T, ?, List<T>> toList(),Collector<T, ?, Set<T>> toSet()等操作.这些操作最终都会调用如下构造函数构造出collector对象,因此掌握该本质是最佳的学习方式.

CollectorImpl(Supplier<A> supplier,
                      BiConsumer<A, T> accumulator,
                      BinaryOperator<A> combiner,
                      Function<A,R> finisher,
                      Set<Characteristics> characteristics) {
            this.supplier = supplier;
            this.accumulator = accumulator;
            this.combiner = combiner;
            this.finisher = finisher;
            this.characteristics = characteristics;
        }

Supplier类似reduce中的u,接收一个元数据,BiConsumer则是操作数据,BinaryOperator并发下聚合,finisher完成时的转换操作,Set应该按照定义是优化一些操作中的转换.如下面的toList()操作,其finish操作为castingIdentity().

public static <T>
 Collector<T, ?, List<T>> toList() {
     return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
                                (left, right) -> { left.addAll(right); return left; },
                                CH_ID);
 }

再看toMap的实现

public static <T, K, U, M extends Map<K, U>>
Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
                            Function<? super T, ? extends U> valueMapper,
                            BinaryOperator<U> mergeFunction,
                            Supplier<M> mapSupplier) {
    BiConsumer<M, T> accumulator
            = (map, element) -> map.merge(keyMapper.apply(element),
                                          valueMapper.apply(element), mergeFunction);
    return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID);
}

Function作为转换函数提供了key和value的转换,BinaryOperator提供了重复key合并策略,mapSupplier则表示最终收集到的容器.那么使用就很简单了

HashMap<Character, String> map = testData.stream()
        .collect(Collectors.toMap(x -> x.charAt(0), Function.identity()
            , (v1, v2) -> v2, HashMap::new));

其他还有很多方法,就不一一叙述,主要是了解这些接口,知道他所拥有的功能,以及组合的意义,即可很好的掌握Java中的函数式编程.

个人博客 mrdear.cn ,欢迎交流

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大数据学习笔记

Java程序设计(Java9版):第2章 数据类型与运算符(Data types and Operators)

第2章 数据类型与运算符(Data types and Operators) I think everybody in this country should ...

2445
来自专栏三好码农的三亩自留地

5分钟彻底理解-Java自动装箱、拆箱

当表格中左边列出的基础类型与它们的包装类有如下几种情况时,编译器会自动帮我们进行装箱或拆箱.

2392
来自专栏老马说编程

(54) 剖析Collections - 设计模式 / 计算机程序的思维逻辑

上节我们提到,类Collections中大概有两类功能,第一类是对容器接口对象进行操作,第二类是返回一个容器接口对象,上节我们介绍了第一类,本节我们介绍第二类。...

2479
来自专栏Ryan Miao

java中复制对象通过反射或序列化

在使用缓存读取数据后修改发现缓存被修改。于是找了下复制对象的方法。 关于对象克隆 ---- 按我的理解,对象是包含引用+数据。通常变量复制都是将引用传递过去。比...

3189
来自专栏Android机器圈

递归 —— 二分查找法 —— 归并排序

二分法就是把一个数组折半查找,再折半直到找到数据位置,或者无数据位置。比如说1-100,你选的值是23,那么范围写法就是(索引写法类似)

1714
来自专栏有趣的Python

1-Java面向对象-面向对象

通过前面的学习我们对于java的语法结构有了一定的认识,掌握了分支结构,循环结构等常用的程序逻辑,也能运用这些知识解决一些简单问题。

1581
来自专栏用户2442861的专栏

JavaScript 正则表达式上——基本语法

JavaScript种正则表达式有两种定义方式,定义一个匹配类似 <%XXX%> 的字符串

591
来自专栏LanceToBigData

JavaSE(八)之集合概述

前几天其实一直在学习关于linux的内容和kvm虚拟化的知识。今天有时间来回顾一下集合相关的知识,接下来我将带大家一起来回顾一起集合关联的知识。 不要辜负自己花...

1845
来自专栏cmazxiaoma的架构师之路

Java数据结构和算法(2)--《Java数据结构和算法》第二版 Robert lafore第二章【数组】编码作业

2023
来自专栏机器学习入门

挑战程序竞赛系列(89):3.6平面扫描(3)

挑战程序竞赛系列(89):3.6平面扫描(3) 传送门:POJ 3292: Rectilinear polygon 题意参考hankcs: http://w...

2025

扫码关注云+社区