当前位置: 首页 > news >正文

Java8实用技能

大概一年多之前,我对java8的理解还仅限一些只言片语的文章之上,后来出于对函数式编程的兴趣,买了本参考书看了一遍,然后放在了书架上,后来,当我接手大客户应用的开发工作之后,java8的一些工具,对我的效率有了不小的提升,因此想记录一下java'8的一些常用场景,我希望这会成为一个小字典,能让我免于频繁翻书,但是总能找到自己想找的知识。

用于举例的model:

@Data
public class Apple {
    private Long appleId;
    private String appleName;
    private Float appleWeight;
    private Integer appleClassic;
}
复制代码

一、Java 8 Lambda 表达式

这无疑是最常用的功能之一,其实lambda表达式的作用,应该就是简洁明了,实际上是用最短的字符,通过类型推导,语法糖等方式去对编译器描述清楚这段代码的功能,这和泛型有点相似,对于编程人员来说,一定程度上也提高了编程效率和代码可读性。

如常用的lambda表达式: process(()->System.out.println("this is so cool!"))

例如对苹果重量排序:

   List<Apple> apples = Lists.newArrayList();
        for (int i = 1; i < 10; i++) {
            Apple apple = new Apple();
            apples.add(apple);
        }
        apples.sort(Comparator.comparing(Apple::getAppleWeight));


反序:
        apples.sort(Comparator.comparing(Apple::getAppleWeight).reversed());


重量相同时:比较等级:



  apples.sort(Comparator
                .comparing(Apple::getAppleWeight)
                .reversed()



谓词复合查询:


  Predicate<Apple> a = apple -> apple.getAppleWeight() > 10;
        weight10.or(apple -> apple.getAppleClassic() > 2)
                .and(apple -> StringUtils.equalsIgnoreCase(apple.getAppleName(), "优质苹果"));
                
可以看做(a||b)&&c


函数复合:


        Function<Apple, Float> f = a -> a.getAppleWeight() + 1;
        Function<Float, Float> g = a -> a * 2;
        Function<Apple, Float> h = f.andThen(g);
        
    数学写作 h=g(f(x))

  Function<Apple, Float> g = a -> a.getAppleWeight() + 1;
        Function<Float, Float> f = a -> a * 2;
        Function<Apple, Float> h = f.compose(g);
 数学写作 h=f(g(x))
复制代码

小结:java8实际上想传递函数,函数是什么?是一个映射,可以看做x->y,输入x然后映射到值y的过程, java无法摆脱一切皆是对象的思想,因此函数式依附在对象上传递的,因此也有了下面的说法,方法引用,以及函数式接口,让函数随着对象传递,为了函数式编程,甚至专门写一个接口---对象来传递函数。然而,函数才是主角。

二、Java 8 方法引用

方法引用十分简单,其实也是将方法作为参数传递。使用::域作用符,将一段方法传递。 举例:Apple::getAppleId String::subString System.out::println

三、Java 8 函数式接口 函数式编程

利用java进行函数式编程主要就是利用函数式接口,但是函数式接口在java8之前就有一些了,就例如多线程的runnable,但是8以前是没有lambda表达式的,所以只能使用匿名内部类,在用过lambda表达式的人看来,那是相当臃肿的,8更新了lambda表达式,这就使函数式编程更上一层楼.

java8的函数式接口为我们传递函数提供了工具,我们可以自己定义函数式接口,然后让其他人,或者是java API调用。 关于函数接口,需要记住的就是两件事: 函数接口是行为的抽象; 函数接口是数据转换器。

四、Java 8 Stream

在我接触到java8流式处理的时候,我的第一感觉是流式处理让集合操作变得简洁了许多,通常我们需要多行代码才能完成的操作,借助于流式处理可以在一行中实现。其本质是,将一些原本开发者需要做的处理如迭代等,放在了java库里,让我们只关心自己的业务逻辑,比如我们希望对一个包含整数的集合中筛选出所有的偶数,并将其封装成为一个新的List返回,那么在java8之前,我们需要通过如下代码实现:

过去:
List<Integer> evens = new ArrayList<>();
for (final Integer num : nums) {
    if (num % 2 == 0) {
        evens.add(num);
    }
}

stream实现:
List<Integer> evens = nums.stream().filter(num -> num % 2 == 0).collect(Collectors.toList());



我们需要取出10个等级高于3的苹果,跳过其中两个,按重量排序,去重,然后取出苹果的Name,然后取出名字的每个字符:
 List<String> appleName = apples.parallelStream()
                .filter(a -> a.getAppleClassic() < 2)
                .sorted(Comparator.comparing(Apple::getAppleWeight))
                .map(Apple::getAppleName)
                .map(s -> s.split(""))
                .limit(10)
                .skip(2)
                .distinct()
                .flatMap(Arrays::stream)
                .collect(Collectors.toList());   
                
构造AppleId ApppleName Map:
        Map<Long, String> appleIdMap = apples.stream()
                .collect(Collectors.toMap(Apple::getAppleId, Apple::getAppleName, (s, s2) -> s.length() > s2.length() ? s : s2));
       
       
 谓词查找:      
 if (appleName.stream().anyMatch(a -> StringUtils.equalsIgnoreCase(a, "一级苹果")));
        if (appleName.stream().allMatch(a -> StringUtils.equalsIgnoreCase(a, "一级苹果")));
        if (appleName.stream().noneMatch(a -> StringUtils.equalsIgnoreCase(a, "一级苹果")));
     


短路查找:
        appleName.stream()
                .filter(a -> StringUtils.equalsIgnoreCase(a, "一级苹果"))
                .findAny()
                .ifPresent(System.out::println);
findfirst在并行时限制多一些,如果不在意返回的是哪个元素,使用findAny。
                
       
求和:         
    apples.stream()
    .map(Apple::getAppleWeight)
    .reduce(0F, (a, b) -> a + b);

计数:
        apples.stream().count();

                
复制代码

使用stream的好处: 1.更简洁,更易读 2.可复合,更灵活 3.可并行

五、Java 8 Optional 类

Optional着重为解决java的NPE问题是Java8提供的为了解决null安全问题的一个API。善用Optional可以使我们代码中很多繁琐、丑陋的设计变得十分优雅。

使用Optional,我们就可以把下面这样的代码进行改写:
public static String getName(User u) {
    if (u == null)
        return "Unknown";
    return u.name;
}

不过,千万不要改写成这副样子。

public static String getName(User u) {
    Optional<User> user = Optional.ofNullable(u);
    if (!user.isPresent())
        return "Unknown";
    return user.get().name;
}

这样才是正确使用Optional的姿势。那么按照这种思路,我们可以安心的进行链式调用,而不是一层层判断了。
public static String getName(User u) {
    return Optional.ofNullable(u)
                    .map(user->user.name)
                    .orElse("Unknown");
}
看一段代码:

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.");
}
让我们看看经过Optional加持过后,这些代码会变成什么样子。
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."));
}

还有很多不错的使用姿势,比如为空则不打印可以这么写:

string.ifPresent(System.out::println);

复制代码

参考资料:《Java 8 in Action: Lambdas, streams, and functional-style programming》 Raoul-gabriel Urma (作者), Mario Fusco (作者), Alan Mycroft (作者)

作者:文烁 点击 查看更多详情

相关文章:

  • 悉尼将举办农历节庆典迎猪年
  • 2018,你的专属云上回忆录
  • transform matrix阅读后的理解
  • 15对金婚夫妻亮相《挑战不可能之加油中国》分享婚姻心得
  • 一文带你快速了解,python是如何解析XML文件
  • 验证码图片识别相关博客收集
  • 系统可用性几个9
  • 15分钟在笔记本上搭建 Kubernetes + Istio开发环境
  • Docker镜像细节
  • 河北优化口岸营商环境 促进跨境贸易便利化
  • 不仅有Ubuntu,这家公司的Ubuntu Core预计使用翻倍
  • 沈阳机场海关设置春运申报窗口 确保年货鲜活可靠
  • GitNote 基于 Git 的跨平台笔记软件正式发布
  • xkb 第6章 server 中的键事件处理
  • PAT A1092
  • [译]前端离线指南(上)
  • 【跃迁之路】【585天】程序员高效学习方法论探索系列(实验阶段342-2018.09.13)...
  • Asm.js的简单介绍
  • CSS相对定位
  • Elasticsearch 参考指南(升级前重新索引)
  • JavaScript的使用你知道几种?(上)
  • Java编程基础24——递归练习
  • Logstash 参考指南(目录)
  • React中的“虫洞”——Context
  • scrapy学习之路4(itemloder的使用)
  • 工作中总结前端开发流程--vue项目
  • 前端面试总结(at, md)
  • 浅谈Kotlin实战篇之自定义View图片圆角简单应用(一)
  • 实战|智能家居行业移动应用性能分析
  • 这几个编码小技巧将令你 PHP 代码更加简洁
  • 智能合约Solidity教程-事件和日志(一)
  • LevelDB 入门 —— 全面了解 LevelDB 的功能特性
  • ​第20课 在Android Native开发中加入新的C++类
  • #LLM入门|Prompt#3.3_存储_Memory
  • #图像处理
  • (2)MFC+openGL单文档框架glFrame
  • (4)事件处理——(7)简单事件(Simple events)
  • (C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...
  • (八)光盘的挂载与解挂、挂载CentOS镜像、rpm安装软件详细学习笔记
  • (附源码)spring boot儿童教育管理系统 毕业设计 281442
  • (原創) 如何讓IE7按第二次Ctrl + Tab時,回到原來的索引標籤? (Web) (IE) (OS) (Windows)...
  • (转)c++ std::pair 与 std::make
  • (转载)虚幻引擎3--【UnrealScript教程】章节一:20.location和rotation
  • .net core使用RPC方式进行高效的HTTP服务访问
  • .net/c# memcached 获取所有缓存键(keys)
  • .net流程开发平台的一些难点(1)
  • .NET实现之(自动更新)
  • .net下简单快捷的数值高低位切换
  • @SuppressLint(NewApi)和@TargetApi()的区别
  • [ai笔记3] ai春晚观后感-谈谈ai与艺术
  • [BUAA软工]第一次博客作业---阅读《构建之法》
  • [GXYCTF2019]禁止套娃
  • [HarekazeCTF2019]encode_and_encode 不会编程的崽
  • [iOS开发]事件处理与响应者链
  • [LeetCode]—Permutations 求全排列