关于code:简化业务代码开发看Lambda表达式如何将代码封装为数据

37次阅读

共计 12110 个字符,预计需要花费 31 分钟才能阅读完成。

摘要:在云服务业务开发中,长于应用代码新个性,往往能让开发效率大大晋升,这里简略介绍下 lambad 表达式及函数式接口个性。

在云服务业务开发中,长于应用代码新个性,往往能让开发效率大大晋升,这里简略介绍下 lambad 表达式及函数式接口个性。

1.Lambda 表达式

Lambda 表达式也被称为箭头函数、匿名函数、闭包。他容许把函数作为一个办法的参数(函数作为参数传递到办法中),体现出轻量级函数式编程思维。

为什么引入 lambda?

Model Code as Data,编码及数据,尽可能轻量级的将代码封装为数据。

解决方案:接口 & 实现类(匿名外部类)

存在问题:语法冗余,this 关键字、变量捕捉、数据管制等

public static void main (String[] args){
    // 1. 传统模式下,新线程的创立
    new Thread (new Runnable() {
        @Override 
        public void run() {System.out.println("threading..." + Thread.currentThread().getId())
        }
    }).start();
     // 2. lambda 表达式优化线程模式
    new Thread(()->{System.out.println("lambda threading..." + Thread.currentThread().getId());
    })
    
}
复制代码
  1. 不是解决未知问题的新技术
  2. 对现有问题的语义化优化
  3. 须要依据理论需要思考性能问题

2. 函数式接口(Functional Interface)

函数式接口就是 Java 类型零碎中的接口,是只蕴含一个形象办法的非凡接口(能够有很多非形象办法)。

语言化检测注解:@FunctionalInterface 检测合法性

java1.8 反对接口内蕴含:形象办法、默认接口办法、动态接口办法、来自 Object 继承的办法

/**
 * 用户身份认证标记接口
 */
@FunctionalInterface
public interface IUserCredential {
 
    /**
     * 通过用户账号,验证用户身份信息的接口
     * @param username 要验证的用户账号
     * @return 返回身份信息[系统管理员、用户管理员、普通用户]
     */
    String verifyUser(String username);
    
    default String getCredential(String username) {if ("admin".equals(username)) {return "admin + 系统管理员用户";} else if("manager".equals(username)){return "manager + 用户管理员用户";} else {return "commons + 一般会员用户";}    }    String toString();     /**     * 音讯合法性验证办法     * @param msg 要验证的音讯     * @return 返回验证后果     */    static boolean verifyMessage(String msg) {if (msg != null) {return true;}        return false;    }}   // 匿名外部类,实现接口的形象办法        IUserCredential ic = new IUserCredential() {            @Override
          public String verifyUser(String username) {return "admin".equals(username)?"管理员":"会员";            }        };        // lambda 表达式是函数式接口的一种简略实现               IUserCredential ic2 = (username) -> {return "admin".equals(username)?"lbd 管理员": "lbd 会员";        };
复制代码

JDK 1.8 之前已有的函数式接口:

  •  java.lang.Runnable
  • java.util.concurrent.Callable
  • java.security.PrivilegedAction
  • java.util.Comparator
  • java.io.FileFilter
  • more

JDK 1.8 新减少的函数接口:

  • java.util.function

    /*
        java.util.function 提供了大量的函数式接口
        Predicate 接管参数 T 对象,返回一个 boolean 类型后果
        Consumer 接管参数 T 对象,没有返回值
        Function 接管参数 T 对象,返回 R 对象
        Supplier 不承受任何参数,间接通过 get()获取指定类型的对象
        UnaryOperator 接口参数 T 对象,执行业务解决后,返回更新后的 T 对象
        BinaryOperator 接口接管两个 T 对象,执行业务解决后,返回一个 T 对象
         */
       Predicate<String> pre = (String username) -> {return "admin".equals(username);
        };
        System.out.println(pre.test("manager"));
     
        Consumer<String> con = (String message) -> {System.out.println("要发送的音讯:" + message);
        };
        con.accept("lambda expression.");
     
        Function<String, Integer> fun = (String gender) -> {return "male".equals(gender)?1:0;
        };
        System.out.println(fun.apply("male"));
     
        Supplier<String> sup = () -> {return UUID.randomUUID().toString();};
        System.out.println(sup.get());
    
         UnaryOperator<String> uo = (String img)-> {img += "[100x200]";
            return img;
        };
        System.out.println(uo.apply("原图 --"));
    
        BinaryOperator<Integer> bo = (Integer i1, Integer i2) -> {return i1 > i2? i1: i2;};
        System.out.println(bo.apply(12, 13));
    复制代码

3.lambda 表达式的根本语法

根本语法

  • 申明:就是和 lambda 表达式绑定的接口类型
  • 参数:蕴含在一对圆括号中,和绑定的接口中的形象办法中的参数个数及程序统一。
  • 操作符:->
  • 执行代码块:蕴含在一对大括号中,呈现在操作符号的右侧

[接口申明] = (参数) -> {执行代码块};

// 没有参数,没有返回值的 lambda 表达式绑定的接口
    interface ILambda1{void test();
    }
 
    // 带有参数,没有返回值的 lambda 表达式
    interface ILambda2{void test(String name, int age);
    }
 
    // 带有参数,带有返回值的 lambda 表达式
    interface ILambda3 {int test(int x, int y);
    }
ILambda1 i1 = () -> System.out.println("hello boys!");
        i1.test();
 
        ILambda2 i21 = (n,  a) -> {System.out.println(n + "say: my year's old is " + a);
        };
        i21.test("jerry", 18);
 
        ILambda2 i22 = (n, a) -> 
            System.out.println(n + "说:我往年" + a + "岁了.");
        
        i22.test("tom", 22);
 
        ILambda3 i3 = (x, y) -> {
            int z = x + y;
            return z;
        };
        System.out.println(i3.test(11, 22));
 
        ILambda3 i31 = (x, y) -> x + y;
        System.out.println(i31.test(100, 200));
复制代码

总结:

  • lambda 表达式,必须和接口进行绑定。
  • lambda 表达式的参数,能够附带 0 个到 n 个参数,括号中的参数类型能够不必指定,jvm 在运行时,会主动依据绑定的形象办法中的参数进行推导。
  • lambda 表达式的返回值,如果代码块只有一行,并且没有大括号,不必写 return 关键字,单行代码的执行后果,会主动返回。如果增加了大括号,或者有多行代码,必须通过 return 关键字返回执行后果。

变量捕捉

  • 匿名外部类型变量捕捉
  • lambda 表达式变量捕捉

     // 1. 匿名外部类型中对于变量的拜访
             String s1 = "全局变量";
             public void testInnerClass() {
                 String s2 = "局部变量";
              
                 new Thread(new Runnable() {
                     String s3 = "外部变量";
                     @Override
                     public void run() {
                         // 拜访全局变量
         //              System.out.println(this.s1);// this 关键字~ 示意是以后外部类型的对象(报错)System.out.println(s1);
              
                         System.out.println(s2);// 局部变量的拜访,不能对局部变量进行数据的批改 final
         //              s2 = "hello";
              
                         System.out.println(s3);
                        System.out.println(this.s3);
                     }
                 }).start();}
              
             // 2. lambda 表达式变量捕捉
             public void testLambda() {
                 String s2 = "局部变量 lambda";
              
                 new Thread(() -> {
                     String s3 = "外部变量 lambda";
              
                     // 拜访全局变量
    
                     // 不再建设对象域
                     System.out.println(this.s1);// this 关键字,示意的就是所属办法所在类型的对象
                     // 拜访局部变量                     System.out.println(s2);
         //          s2 = "hello";// 不能进行数据批改,默认推导变量的修饰符:final
                     System.out.println(s3);
                     s3 = "labmda 外部变量间接批改";
                     System.out.println(s3);
                }).start();}
    复制代码

总结:Lambda 表达式优化了匿名外部类类型中的 this 关键字,不再独自建设对象作用域,表达式自身就是所属类型对象的一部分,在语法语义上应用更加简洁。

类型查看

对于语法雷同的表达式,Jvm 在运行的过程中,在底层通过解释及重构,进行类型的主动推导。

  • 表达式类型查看
  • 参数类型查看

办法重载

interface Param1 {void outInfo(String info);
    }
 
    interface Param2 {void outInfo(String info);
    }
// 定义重载的办法
    public void lambdaMethod(Param1 param) {param.outInfo("hello param1 imooc!");
    }
    public void lambdaMethod(Param2 param) {param.outInfo("hello param2 imooc");
    }
test.lambdaMethod(new Param1() {
            @Override
            public void outInfo(String info) {System.out.println(info);
            }
        });
 
        test.lambdaMethod(new Param2() {
            @Override
            public void outInfo(String info) {System.out.println("------");
                System.out.println(info);
            }
        });
 
        /*
        lambda 表达式存在类型查看 -> 主动推导 lambda 表达式的指标类型
        lambdaMethod() -> 办法 -> 重载办法
                -> Param1  函数式接口
                -> Param2  函数式接口
                调用办法 -> 传递 Lambda 表达式 -> 主动推导 -> 
                   -> Param1 | Param2
         */
//               报错 Ambigus Method call
//        test.lambdaMethod((String info) -> {//            System.out.println(info);
//        });
复制代码

总结:呈现办法重载的类型中参数都是函数式接口的状况,需应用匿名外部类实现代替 lambda 表达式。

底层构建原理

public class Test{public static void main(String args[]){ITest it = (message) -> System.out.println(message);
               it.markUp("lambda!");  
        // new Test$$Lambda$1().markUp("lambda");
        } 
}
interface ITest{void markUp(String msg);
}
 
复制代码

javac Test.java

javap -p Test.class (javap 反解析工具 - p 显示所有类与成员)

  •  java -Djdk.internal.lambda.dumpProxyClasses Test

  1. 申明一个公有静态方法,对 Lambda 表达式做一个具体的办法实现
  2. 申明一个 final 外部类型并实现接口
  3. 在实现接口后的重写办法中利用外部类调用该公有静态方法

 4. 办法援用

办法援用提供了十分有用的语法,能够间接援用已有 Java 类或对象(实例)的办法或结构器。与 lambda 联结应用,办法援用能够使语言的结构更紧凑简洁,缩小冗余代码。

  • 静态方法援用
  • 实例办法援用
  • 构造方法援用

5.Stream

  • 新增加的 Stream 流—是一个来自数据源的元素队列并反对聚合操作。把真正的函数式编程格调引入到 Java 中。
  • 不存储数据,也不批改原始源。
  • Stream 应用一种相似用 SQL 语句从数据库查问数据的直观形式来提供一种对 Java 汇合运算和表白的高阶形象。
  • Stream API 能够极大进步 Java 程序员的生产力,让程序员写出高效率、洁净、简洁的代码。
  • 这种格调将要解决的元素汇合看作一种流,流在管道中传输,并且能够在管道的节点上进行解决,比方筛选,排序,聚合等。
  • 元素流在管道中通过两头操作(intermediate operation)的解决,最初由最终操作 (terminal operation) 失去后面解决的后果。

// 1. for 循环实现 List list = new ArrayList(); for (String s : list) {if (s.length() > 3) {lista.add(s); } } System.out.println(lista);

// 2. 迭代器实现
List<String> listb = new ArrayList<>();
Iterator<String> it = list.iterator();
while(it.hasNext()) {String s = it.next();
    if(s.length() > 3) {listb.add(s);
    }
}
System.out.println(listb);
 
// 3. stream 实现
List listc = list.stream().filter(s->s.length()>3)
    .collect(Collectors.toList());
System.out.println(listc);
复制代码

几者关系

  • lambda 表达式是传统办法的语法糖,简化并且革新传统外部类实现设计方案的另一种实现模式。
  • 办法援用又是 lambda 根底上的语法糖,和 Stream 没有关系,简化办法调用的。
  • Stream 是针对数据和汇合的强化优化操作,能够和 lambda 联合起来简化编码过程。

常见 API 介绍

1. 聚合操作

2.Stream 的解决流程

  • 数据源
  • 数据转换[可一到屡次转换]
  • 获取后果

3. 获取 Stream 对象

  • 从汇合或者数组中获取

Collection.stream(), 如 list.stream()

Collection.parallelstream(), 取得反对并发解决的流

Arrays.stream(T t)

  • BufferReader

BufferReader.lines()-> stream()

  • 动态工厂

java.util.stream.IntStream.range()..

java.nio.file.Files.walk()..

  • 自定构建

java.util.Spliterator

  • 更多的形式

Random.ints()

Pattern.spiltAsStream()..

4. 两头操作 API{intermediate}:

  • 操作后果是一个 Stream 对象,所以两头操作可有一个或多个间断的两头操作,须要留神的是两头操作只记录操作形式,不做具体执行,直到完结操作产生时,才做数据的最终执行。
  • 两头操作就是业务逻辑解决
  • 操作过程分为有状态和无状态

无状态:即解决数据时,不受前置两头操作的影响

  • map/filter/peek/parallel/sequential/unordered
  • 有状态:即解决数据时,受前置两头操作的影响
  • distant/sorted/limit/skip

5. 终结操作 | 完结操作{Terminal}

一个 steam 对象只能有一个 Terminal 操作。这个操作不可逆,一旦产生,就会实在解决数据生成对应后果

  • 非短路操作: 以后的 Stream 对象必须解决完汇合中所有的数据,能力失去处理结果

forEach/forEachOrdered/toArray/reduce/collect/min/max/count/iterator

  • 短路操作: 以后的 Stream 对象在处理过程中,一旦满足某个条件,就能够失去后果

    anyMatch/AllMatch/noneMatch/findfirst/findAny 等

short-circuiting : 在无限大的 stream 中返回无限大的 stream 须要蕴含短路操作是有必要的

Stream 转换

 // 1. 批量数据 -> Stream 对象
          // 多个数据
          Stream stream = Stream.of("admin", "tom", "jerry");
   
          // 数组
          String [] strArrays = new String[] {"xueqi", "biyao"};
          Stream stream2 = Arrays.stream(strArrays);
   
          // 列表
          List<String> list = new ArrayList<>();
          list.add("aaa");
          list.add("bbb");
          list.add("ccc");
          Stream stream3 = list.stream();
   
          // 汇合
          Set<String> set = new HashSet<>();
          set.add("aaa");
          set.add("bbb");
          set.add("ccc");
          Stream stream4 = set.stream();   
          // Map
          Map<String, Integer> map = new HashMap<>();
          map.put("tom", 1000);
          map.put("jerry", 1200);
          map.put("shuke", 1000);
          Stream stream5 = map.entrySet().stream();   
      //2. Stream 对象对于根本数据类型的性能封装
          //int / long / double
          IntStream.of(new int[] {10, 20, 30}).forEach(System.out::println); // 只做一次拆箱装箱
          IntStream.range(1, 5).forEach(System.out::println);

          IntStream.rangeClosed(1, 5).forEach(System.out::println);  
     // 3. Stream 对象 --> 转换失去指定的数据类型
          // 数组
          Object [] objx = stream.toArray(String[]::new);
   
          // 字符串
          String str = stream.collect(Collectors.joining()).toString();
          System.out.println(str);
   
          // 列表
          //List<String> listx = (List<String>) stream.collect(Collectors.toList());
          System.out.println(listx);
   
          // 汇合
          //Set<String> setx = (Set<String>) stream.collect(Collectors.toSet());
          System.out.println(setx);
   
          // Map
          //Map<String, String> mapx = (Map<String, String>)                                               stream.collect(Collectors.toMap(x->x, y->"value:"+y));
       System.out.println(mapx);
复制代码

Stream 常见操作

// Stream 中常见的 API 操作
        List<String> accountList = new ArrayList<>();
        accountList.add("tom");
        accountList.add("jerry");
        accountList.add("apha");
        accountList.add("beta");
        accountList.add("shuke");
 
        // map() 两头操作,map()办法接管一个 Functional 接口
        accountList = accountList.stream().map(x->"name:" + x).collect(Collectors.toList());
 
        // filter() 增加过滤条件,过滤符合条件的用户
        accountList = accountList.stream().filter(x-> x.length() > 3).collect(Collectors.toList());
 
        // forEach 增强型循环
        accountList.forEach(x-> System.out.println("forEach->" + x));
 
        // peek() 两头操作,迭代数据实现数据的顺次处理过程
        accountList.stream()
                .peek(x -> System.out.println("peek 1:" + x))
                .peek(x -> System.out.println("peek 2:" + x))
                .forEach(System.out::println);// 合并多个过程 迭代只产生一次
 
        accountList.forEach(System.out::println);
 
        // Stream 中对于数字运算的反对
        List<Integer> intList = new ArrayList<>();
        intList.add(20);
        intList.add(19);
        intList.add(7);
        intList.add(8);
        intList.add(86);
        intList.add(11);
        intList.add(3);
        intList.add(20);
 
        // skip() 两头操作,有状态,跳过局部数据
        intList.stream().skip(3).forEach(System.out::println);
 
        // limit() 两头操作,有状态,限度输入数据量
        intList.stream().skip(3).limit(2).forEach(System.out::println);
 
        // distinct() 两头操作,有状态,剔除反复的数据
        intList.stream().distinct().forEach(System.out::println);
 
        // sorted() 两头操作,有状态,排序
        // max() 获取最大值
        Optional optional = intList.stream().max((x, y)-> x-y);
        System.out.println(optional.get());
        // min() 获取最小值
 
        // reduce() 合并解决数据
        Optional optional2 = intList.stream().reduce((sum, x)-> sum + x);
        System.out.println(optional2.get());
复制代码

6. 案例

问题一:将实例 List 转化为 Map

对于 List

来说,我须要将其形变为 Map<Table.id,Table>,用如下流解决代码

//Table 类
public class DmTable {
    private Integer id;
 
    private String tableName;
 
    private String tableComment;
 
    private Integer datasourceId;
 
    private Integer directoryId;
 
    private Boolean partitionFlag;
    
    private Integer columnNum;
    // ......
}
tableMap=TableList.stream().collect(Collectors.toMap(Table::getId, b -> b);
// 等效于
tableMap=TableList.stream().collect(Collectors.toMap(Table::getId, Function.identity()));// 静态方法 实现 return t -> t;
复制代码

问题二:将汇合分成若干类别

应用问题一中的 Table 类,对于 List

,我须要将其依照 partitionFlag 分类,Collector 提供两种办法 partitioningBy()、groupingBy()。前者分成满足条件与不满足条件两类,后者可按条件分成若干类别的 Map。

 Map<Boolean, List<Table>> tablePartition = tableList
          .stream().collect(Collectors.partitioningBy(item -> item.getPartitionFlag() == true));
复制代码
  • 有的时候,咱们关注的不光是元素还有元素的个数,流解决能够再进行前期解决。

    Map<Boolean, List

tablePartition = tableList .stream().collect(Collectors.partitioningBy(item -> item.getPartitionFlag() == true,Collectors.counting()));

可输入符合要求的个数。

groupingBy()可对字符串长度分组。

List<String> strings=Arrays.asList(“this”,”is”,”a”,”test”);
Map<Integer, List<String>> stringsMap = strings
        .stream().collect(Collectors.groupingBy(String::length);
复制代码

后果输入多分类的 map,key 值为字符串长度。

留神:如果是从数据库获取数据,务必将分组操作放在数据库中执行,java8 新增办法只适宜解决内存中的数据。

问题三:从 list 中失去某个特定的对象

取得 List

中 columnNum 最多的 table 对象

tableList.stream().sorted(comparingInt(Table::getColumnNum)).collect(Collectors.toList()).get(tableList.size() - 1);
复制代码

增加两头操作 reversed() 可获取最小 columnNum 的对象

问题四:失去 Map<Table,Table.columnNum> 中最大 columnNum 的 table

 List<Map.Entry<Table, Integer>> list = new ArrayList(tableMap.entrySet());
Collections.sort(list, (o1, o2) -> (o2.getValue() - o1.getValue()));
list.get(0).getKey();
复制代码

7. 性能与平安

  • 串行 Stream 的性能小于传统的 for 循环、迭代器
  • 并行 Stream 的性能与传统的 for 循环、迭代器差不多,在解决对象(简单数据类型)的状况下,并行性能最佳

    // 整数列表 List lists = new ArrayList(); // 减少数据 for (int i = 0; i < 1000; i++){lists.add(i); }

     // 串行 Stream
          List<Integer> list2 = new ArrayList<>();
          lists.stream().forEach(x->list2.add(x));
          System.out.println(lists.size());
          System.out.println(list2.size());
          // 并行 Stream  线程不平安 失落
          List<Integer> list3 = new ArrayList<>();
          lists.parallelStream().forEach(x-> list3.add(x));
          System.out.println(list3.size());
                  // collect 当并行执行时能够实例化、填充和合并多个两头后果,以放弃可变数据结构的隔离
          List<Integer> list4 = lists.parallelStream().collect(Collectors.toList());
          System.out.println(list4.size());
    复制代码

本文分享自华为云社区《如何善用函数式接口简化云服务业务代码开发》,原文作者:luanzhen。

点击关注,第一工夫理解华为云陈腐技术~

正文完
 0