java函数式编程

今天我们来学习一下java8的新语法 ,有前端基础的同学看起来会更轻松。

1 stream

Java 8 中引入了流(Stream)的概念,这个流和以前我们使用的 IO 中的流并不太相同。
假设我们有一个 List 包含一系列的 Person,Person 有姓名 name 和年龄 age 连个字段。现要求这个列表中年龄大于 20 的人数。
通常按照以前我们可能会这么写:

long count = 0;for (Person p : persons) {    if (p.getAge() > 20) {
        count ++;
    }
}

但如果使用 stream 的话,则会简单很多:

long count = persons.stream()
                    .filter(person -> person.getAge() > 20)
                    .count();

count.forEach((item) -> print item.name)

我们在这些java8的新式写法中看到了es6的身影,原谅我是一个前端gou。。。

我们还能想到mapreduce这种在前端里面在为平常的操作了。

List<String> collected = Stream.of(a, b, hello)
                               .map(string -> string.toUpperCase())
                               .collect(toList());
assertEquals(asList(A, B, HELLO), collected);

请原谅我实在编不下去了,我只想用代码说明一切。。。

List<String> beginningWithNumbers = 
        Stream.of(a, 1abc, abc1)
              .filter(value -> isDigit(value.charAt(0)))
              .collect(toList());
assertEquals(asList(1abc), beginningWithNumbers);

但是我目前为止还没发现js里面有合并的方法

List<Integer> together = Stream.of(asList(1, 2), asList(3, 4))
                               .flatMap(numbers -> numbers.stream())
                               .filter(item -> item%2 == 0)
                               .collect(toList());
assertEquals(asList(2, 4), together);

从这里可以看出flatMap是把2个collection合并为一个流。

以前我们在java7进行比较排序的时候,一般是采用

List<String> list = Arrays.asList(I, love, you, too);
Collections.sort(list, new Comparator<String>(){// 接口名
    @Override
    public int compare(String s1, String s2){// 方法名
        if(s1 == null)            return -1;        if(s2 == null)            return 1;        return s1.length()-s2.length();
    }
});

原始的Comparator接口是这个样子的

public  interface  Comparable<T>{        public  int compareTo(T  o);
}

上述代码通过内部类重载了Comparator接口的compare()方法,实现比较逻辑。采用Lambda表达式可简写如下:

List<String> list = Arrays.asList(I, love, you, too);
Collections.sort(list, (s1, s2) ->{// 省略参数表的类型
    if(s1 == null)        return -1;    if(s2 == null)        return 1;    return s1.length()-s2.length();
});

我们在了解了lambada表达式后这样写更方便

List<Integer> list = Lists.newArrayList(3, 5, 2, 9, 1);int maxInt = list.stream()
                 .max(Integer::compareTo)
                 .get();int minInt = list.stream()
                 .min(Integer::compareTo)
                 .get();
assertEquals(maxInt, 9);
assertEquals(minInt, 1);

下面给大家展示一个更为熟悉的例子

int result = Stream.of(1, 2, 3, 4)
                   .reduce(0, (acc, element) -> acc + element);
assertEquals(10, result);

注意 reduce 的第一个参数,这是一个初始值。0 + 1 + 2 + 3 + 4 = 10。

我们来看下下面这段代码

@Test  public void convertTest() {  
    List<String> collected = new ArrayList<>();  
    collected.add(alpha);  
    collected.add(beta);  
    collected = collected.stream().map(string -> string.toUpperCase()).collect(Collectors.toList());  
    System.out.println(collected);  
}

改造成如下写法

@Test  public void convertTest() {  
    List<String> collected = new ArrayList<>();  
    collected.add(alpha);  
    collected.add(beta);  
    collected = collected.stream().map(String::toUpperCase).collect(Collectors.toCollection(ArrayList::new));//注意发生的变化  
    System.out.println(collected);  
}

我们在js中对一个数组可以随意进行map,filter,reduce等操作,但是在java8中不行。

int sumSize = Stream.of(Apple, Banana, Orange, Pear)
                    .parallel()
                    .map(s -> s.length())
                    .reduce(Integer::sum)
                    .get();
assertEquals(sumSize, 21);

我们可以看到是在用了parallel()之后才可以进行并行流操作。

可以自己把下面代码在ide中运行看一下

public class Hello {
    Runnable r1 = () -> { System.out.println(this); };
    Runnable r2 = () -> { System.out.println(toString()); };    public static void main(String[] args) {        new Hello().r1.run();        new Hello().r2.run();
    }    public String toString() { return Hello Hoolee; }
}

会输出2次Hello Hoolee。

请原谅我必须给大家还原一下事情的真相。

// 使用迭代器删除列表元素ArrayList<String> list = new ArrayList<>(Arrays.asList(I, love, you, too));
Iterator<String> it = list.iterator();while(it.hasNext()){    if(it.next().length()>3) // 删除长度大于3的元素
        it.remove();
}

现在使用removeIf()方法结合匿名内部类,我们可是这样实现:

// 使用removeIf()结合匿名名内部类实现ArrayList<String> list = new ArrayList<>(Arrays.asList(I, love, you, too));
list.removeIf(new Predicate<String>(){ // 删除长度大于3的元素
    @Override
    public boolean test(String str){        return str.length()>3;
    }
});

使用lambada表达式

ArrayList<String> list = new ArrayList<>(Arrays.asList(I, love, you, too));
list.removeIf(str -> str.length()>3); // 删除长度大于3的元素

看一下如何进行字符串拼接。

// 使用Collectors.joining()拼接字符串Stream<String> stream = Stream.of(I, love, you);
String joined = stream.collect(Collectors.joining());// IloveyouString joined = stream.collect(Collectors.joining(,));// I,love,youString joined = stream.collect(Collectors.joining(,, {, }));// {I,love,you}

我们下面来看看如何自定义函数接口

/ 自定义函数接口@FunctionalInterfacepublic interface ConsumerInterface<T>{    void accept(T t);
}

ConsumerInterface<String> consumer = str -> System.out.println(str);

class MyStream<T>{    private List<T> list;
    ...    public void myForEach(ConsumerInterface<T> consumer){// 1
        for(T t : list){
            consumer.accept(t);
        }
    }
}
MyStream<String> stream = new MyStream<String>();
stream.myForEach(str -> System.out.println(str));// 使用自定义函数接口书写Lambda表达式

最后给大家讲一讲optional,Optional是Java8提供的为了解决null安全问题的一个API。善用Optional可以使我们代码中很多繁琐、丑陋的设计变得十分优雅。

public static String getName(User u) {    if (u == null)        return Unknown;    return u.name;
}public static String getName(User u) {    return Optional.ofNullable(u)
                    .map(user->user.name)
                    .orElse(Unknown);
}

这种写法是不是看起来更优雅。

这样才是正确使用Optional的姿势。那么按照这种思路,我们可以安心的进行链式调用,而不是一层层判断了。看一段代码:

public static String getChampionName(Competition comp) throws IllegalArgumentException {    if (comp != null) {
        CompResult result = comp.getResult();        if (result != null) {
            User champion = result.getChampion();            if (champion != null) {                return champion.getName();
            }
        }
    }    throw new IllegalArgumentException(The value of param comp isn't available.);
}public static String getChampionName(Competition comp) throws IllegalArgumentException {    return Optional.ofNullable(comp)
            .map(c->c.getResult())
            .map(r->r.getChampion())
            .map(u->u.getName())
            .orElseThrow(()->new IllegalArgumentException(The value of param comp isn't available.));
}

作者:我是上帝可爱多

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 目录 连接 连接池产生原因 连接池实现原理 小结 TEMPERANCE:Eat not to dullness;drink not to elevation.节制
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 一个优秀的工程师和一个普通的工程师的区别,不是满天飞的架构图,他的功底体现在所写的每一行代码上。-- 毕玄 1. 命名风格 【书摘】类名用 UpperCamelC
今天犯了个错:“接口变动,伤筋动骨,除非你确定只有你一个人在用”。哪怕只是throw了一个新的Exception。哈哈,这是我犯的错误。一、接口和抽象类类,即一个对象。先抽象类,就是抽象出类的基础部分,即抽象基类(抽象类)。官方定义让人费解,但是记忆方法是也不错的 —包含抽象方法的类叫做抽象类。接口
Writer :BYSocket(泥沙砖瓦浆木匠)微 博:BYSocket豆 瓣:BYSocketFaceBook:BYSocketTwitter :BYSocket一、引子文件,作为常见的数据源。关于操作文件的字节流就是 —FileInputStream&amp;FileOutputStream。
作者:泥沙砖瓦浆木匠网站:http://blog.csdn.net/jeffli1993个人签名:打算起手不凡写出鸿篇巨作的人,往往坚持不了完成第一章节。交流QQ群:【编程之美 365234583】http://qm.qq.com/cgi-bin/qm/qr?k=FhFAoaWwjP29_Aonqz
本文目录 线程与多线程 线程的运行与创建 线程的状态 1 线程与多线程 线程是什么? 线程(Thread)是一个对象(Object)。用来干什么?Java 线程(也称 JVM 线程)是 Java 进程内允许多个同时进行的任务。该进程内并发的任务成为线程(Thread),一个进程里至少一个线程。 Ja
Writer :BYSocket(泥沙砖瓦浆木匠)微 博:BYSocket豆 瓣:BYSocketFaceBook:BYSocketTwitter :BYSocket在面向对象编程中,编程人员应该在意“资源”。比如?1String hello = &quot;hello&quot;; 在代码中,我们
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 这是泥瓦匠的第103篇原创 《程序兵法:Java String 源码的排序算法(一)》 文章工程:* JDK 1.8* 工程名:algorithm-core-le
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 目录 一、父子类变量名相同会咋样? 有个小故事,今天群里面有个人问下面如图输出什么? 我回答:60。但这是错的,答案结果是 40 。我知错能改,然后说了下父子类变
作者:泥瓦匠 出处:https://www.bysocket.com/2021-10-26/mac-create-files-from-the-root-directory.html Mac 操作系统挺适合开发者进行写代码,最近碰到了一个问题,问题是如何在 macOS 根目录创建文件夹。不同的 ma
作者:李强强上一篇,泥瓦匠基础地讲了下Java I/O : Bit Operation 位运算。这一讲,泥瓦匠带你走进Java中的进制详解。一、引子在Java世界里,99%的工作都是处理这高层。那么二进制,字节码这些会在哪里用到呢?自问自答:在跨平台的时候,就凸显神功了。比如说文件读写,数据通信,还
1 线程中断 1.1 什么是线程中断? 线程中断是线程的标志位属性。而不是真正终止线程,和线程的状态无关。线程中断过程表示一个运行中的线程,通过其他线程调用了该线程的 方法,使得该线程中断标志位属性改变。 深入思考下,线程中断不是去中断了线程,恰恰是用来通知该线程应该被中断了。具体是一个标志位属性,
Writer:BYSocket(泥沙砖瓦浆木匠)微博:BYSocket豆瓣:BYSocketReprint it anywhere u want需求 项目在设计表的时候,要处理并发多的一些数据,类似订单号不能重复,要保持唯一。原本以为来个时间戳,精确到毫秒应该不错了。后来觉得是错了,测试环境下很多一
纯技术交流群 每日推荐 - 技术干货推送 跟着泥瓦匠,一起问答交流 扫一扫,我邀请你入群 纯技术交流群 每日推荐 - 技术干货推送 跟着泥瓦匠,一起问答交流 扫一扫,我邀请你入群 加微信:bysocket01
Writer:BYSocket(泥沙砖瓦浆木匠)微博:BYSocket豆瓣:BYSocketReprint it anywhere u want.文章Points:1、介绍RESTful架构风格2、Spring配置CXF3、三层初设计,实现WebService接口层4、撰写HTTPClient 客户
Writer :BYSocket(泥沙砖瓦浆木匠)什么是回调?今天傻傻地截了张图问了下,然后被陈大牛回答道“就一个回调…”。此时千万个草泥马飞奔而过(逃哈哈,看着源码,享受着这种回调在代码上的作用,真是美哉。不妨总结总结。一、什么是回调回调,回调。要先有调用,才有调用者和被调用者之间的回调。所以在百
Writer :BYSocket(泥沙砖瓦浆木匠)一、什么大小端?大小端在计算机业界,Endian表示数据在存储器中的存放顺序。百度百科如下叙述之:大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加
What is a programming language? Before introducing compilation and decompilation, let&#39;s briefly introduce the Programming Language. Programming la
Writer :BYSocket(泥沙砖瓦浆木匠)微 博:BYSocket豆 瓣:BYSocketFaceBook:BYSocketTwitter :BYSocket泥瓦匠喜欢Java,文章总是扯扯Java。 I/O 基础,就是二进制,也就是Bit。一、Bit与二进制什么是Bit(位)呢?位是CPU
Writer:BYSocket(泥沙砖瓦浆木匠)微博:BYSocket豆瓣:BYSocket一、前言 泥瓦匠最近被项目搞的天昏地暗。发现有些要给自己一些目标,关于技术的目标:专注很重要。专注Java 基础 + H5(学习) 其他操作系统,算法,数据结构当成课外书博览。有时候,就是那样你越是专注方面越