zoukankan      html  css  js  c++  java
  • 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 (作者)

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

  • 相关阅读:
    [安装程序配置服务器失败]解决SQL Server2000安装失败
    C# 操作 XML 增 删 改 查
    批量修改文件的编码格式
    获取SqlServer2005表结构(字段,主键,外键,递增,描述)
    .NET 特性Attribute[一]
    windows2003远程桌面退出后系统自动注销的解决方法
    SqlServer 无日志文件附加
    接受来自服务器的数据连接时发生超时(30000 毫秒)问题原因及解决方法
    .net中数据集合导出为Excel(支持泛型及显示字段顺序,可自定义显示列名)
    EF中自编写SQL脚本查询结果(适用于复杂SQL逻辑提高查询效率)
  • 原文地址:https://www.cnblogs.com/antfin/p/10307250.html
Copyright © 2011-2022 走看看