摘要:在雲服務業務開發中,善於使用代碼新特性,每每能讓開發效率大大提高,這裏簡單介紹下lambad表達式及函數式接口特性。
在雲服務業務開發中,善於使用代碼新特性,每每能讓開發效率大大提高,這裏簡單介紹下lambad表達式及函數式接口特性。java
Lambda表達式也被稱爲箭頭函數、匿名函數、閉包。他容許把函數做爲一個方法的參數(函數做爲參數傳遞到方法中),體現出輕量級函數式編程思想。程序員
Model Code as Data,編碼及數據,儘量輕量級的將代碼封裝爲數據。數據庫
解決方案:接口&實現類(匿名內部類)express
存在問題:語法冗餘,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()); }) } 複製代碼
函數式接口就是Java類型系統中的接口,是隻包含一個抽象方法的特殊接口(能夠有不少非抽象方法)。segmentfault
語言化檢測註解:@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 以前已有的函數式接口:數據結構
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)); 複製代碼
[接口聲明] = (參數) -> {執行代碼塊};
// 沒有參數,沒有返回值的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表達式變量捕獲
// 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類或對象(實例)的方法或構造器。與lambda聯合使用,方法引用可使語言的構造更緊湊簡潔,減小冗餘代碼。
// 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); 複製代碼
1.聚合操做
2.Stream的處理流程
3.獲取Stream對象
Collection.stream(), 如list.stream()
Collection.parallelstream(), 得到支持併發處理的流
Arrays.stream(T t)
BufferReader.lines()-> stream()
java.util.stream.IntStream.range()..
java.nio.file.Files.walk()..
java.util.Spliterator
Random.ints()
Pattern.spiltAsStream()..
4.中間操做API{intermediate}:
無狀態:即處理數據時,不受前置中間操做的影響
5.終結操做|結束操做{Terminal}
一個steam對象只能有一個Terminal操做。這個操做不可逆,一旦發生,就會真實處理數據生成對應結果
forEach/forEachOrdered/toArray/reduce/collect/min/max/count/iterator
anyMatch/AllMatch/noneMatch/findfirst/findAny等
short-circuiting : 在無限大的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中常見的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()); 複製代碼
問題一:將實例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(); 複製代碼
並行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 。