查看: 960|回復: 0

java8新特性 lambda Stream map(函數式編程)

3萬

主題

3萬

帖子

10萬

積分

管理員

Rank: 9Rank: 9Rank: 9

積分
100197
發表于 2017-12-19 16:21:25

java8新特性 lambda Stream map(函數式編程)
  1. 1.接口的默認方法
  2.         Java 8允許我們給接口添加一個非抽象的方法實現,只需要使用 default關鍵字即可,這個特征又叫做擴展方法
  3.         //Formula表示一個設計 計算公式 的接口
  4.         public interface Formula {
  5.                 //計算
  6.                 double calculate(int a);
  7.                
  8.                 //開方
  9.                 default double sqrt(int a){
  10.                         return Math.sqrt(a);
  11.                 }
  12.         }

  13.         main:
  14.         Formula f = new Formula() {
  15.                 @Override
  16.                 public double calculate(int a) {
  17.                         return a+1;
  18.                 }
  19.         };
  20.         System.out.println(f.calculate(4));
  21.         System.out.println(f.sqrt(8));

  22.         注意:現在接口還可以存在靜態方法,
  23.         可以使用 接口名.靜態方法名 的形式直接調用


  24. 2.Lambda 表達式
  25.         2.1 認識Lambda表達式
  26.         例如:
  27.         public class LambdaTest1 {
  28.        
  29.                 public static void main(String[] args) {
  30.                        
  31.                         //假如一個list機會中的元素要排序
  32.                         List<String> list = Arrays.asList
  33.                         ("hello","tom","apple","bbc");
  34.                         //之前的排序我們可以這樣寫
  35.                         Collections.sort(list, new Comparator<String>(){
  36.                                 @Override
  37.                                 public int compare(String o1, String o2) {
  38.                                         return -o1.compareTo(o2);
  39.                                 }
  40.                         });
  41.                        
  42.                         //使用Lambda表達式
  43.                         Collections.sort(list,(String s1,String s2)->{
  44.                                 return s1.compareTo(s2);
  45.                         });
  46.                        
  47.                         //可以簡寫為
  48.                         //1.大括號里面就一句代碼
  49.                         //2.編譯器可以自動推導出參數類型
  50.                         Collections.sort(list,(s1,s2)->s1.compareTo(s2));
  51.                        
  52.                         System.out.println(list);
  53.                        
  54.                 }
  55.                
  56.         }

  57.         2.2 Functional接口
  58.         “函數式接口”是指僅僅只包含一個抽象方法的接口,每一個該類型的lambda表達式都會被匹配到這個抽象方法。因為 默認方法 不算抽象方法,所以你也可以給你的函數式接口添加默認方法。
  59.         我們可以將lambda表達式當作任意只包含一個抽象方法的接口類型,確保你的接口一定達到這個要求,你只需要給你的接口添加 @FunctionalInterface 注解,編譯器如果發現你標注了這個注解的接口有多于一個抽象方法的時候會報錯的。
  60.         例如:
  61.         public class LambdaTest2 {
  62.        
  63.                 public static void main(String[] args) {
  64.                        
  65.                         LambdaTest2 t = new LambdaTest2();
  66.         //                也可以先創建對象
  67.         //                Action1 a1 = ()->System.out.println("hello");
  68.                        
  69.                         t.test1(()->System.out.println("hello"));
  70.                        
  71.                         //Action2<String,Integer> a2 = (f)->"這個數字是:"+f;
  72.                         //如果參數就一個,那么還可以這樣簡寫 去掉小括號
  73.                         Action2<String,Integer> a2 = f->"這個數字是:"+f;
  74.                         t.test2(a2);
  75.                 }
  76.                 public void test1(Action1 a){
  77.                         a.run();
  78.                 }
  79.                 public void test2(Action2<String,Integer> a){
  80.                         System.out.println(a.run(3));
  81.                 }
  82.                
  83.         }
  84.         //這個注解不加也可以,加上只是為了讓編譯器檢查
  85.         @FunctionalInterface
  86.         interface Action1{
  87.                 public void run();
  88.         }
  89.        
  90.         //這個注解不加也可以,加上只是為了讓編譯器檢查
  91.         @FunctionalInterface
  92.         interface Action2<T,F>{
  93.                 public T run(F f);
  94.         }
  95.        

  96.         注意:lambda表達式無法訪問接口的默認方法

  97.         2.3 方法與構造函數引用
  98.         Java 8 允許你使用 :: 關鍵字來傳遞方法(靜態方法和非靜態方法)
  99.         例如:
  100.         public class LambdaTest3 {
  101.                 public static void main(String[] args) {
  102.                        
  103.                         LambdaTest3 t = new LambdaTest3();
  104.                         //使用Lambda引用類的靜態方法
  105.                         //能引用Integer類中的靜態方法toBinaryString的原因是:
  106.                         //Action3接口中只有一個方法且方法的參數類型和返回值類型
  107.                         //與Integer類中的靜態方法toBinaryString的參數類型、返回類型是一致的
  108.                         Action3 a3 = Integer::toBinaryString;
  109.                         System.out.println(a3.run(4));
  110.                        
  111.                         //使用Lambda引用對象的非靜態方法
  112.                         //能引用對象t中的非靜態方法test的原因是和上面的描述是一致的
  113.                         Action3 aa3 = t::test;
  114.                         System.out.println(aa3.run(4));
  115.                 }
  116.                
  117.                 public String test(int i){
  118.                         return "i="+i;
  119.                 }
  120.         }

  121.         @FunctionalInterface
  122.         interface Action3{
  123.                 public String run(int Integer);
  124.         }


  125.         下面是一個接口中帶泛型的時候特殊例子: 可以使用  類名::非靜態方法  的形式引用方法
  126.         public class LambdaTest6 {
  127.        
  128.                 public static void main(String[] args) {
  129.                        
  130.                         Model m = new Model();
  131.                        
  132.                         //方法有一個參數,然后沒返回類型,這里參數類型會自動識別
  133.                         Action<Model> a1 = (s)->System.out.println("hello");
  134.                         a1.run(m);
  135.                        
  136.                         //注意:如果這里泛型類型不是Model 那么就不能引用Model中的方法
  137.                         //可以引用Model類中任意方法 只要滿足一點:該方法沒有參數
  138.                         //將來run方法中就會調用Model類型對象m的此處引用的方法
  139.                         Action<Model> a2 = Model::test3;
  140.                         a2.run(m);
  141.                        
  142.                         //引用對象m中的test2方法
  143.                         //因為test2方法的參數和返回類型和Action接口的方法完全一致
  144.                         Action<Model> a3 = m::test2;
  145.                         a3.run(m);
  146.                 }
  147.                
  148.         }

  149.         interface Action<T>{
  150.                 public void run(T t);
  151.         }

  152.         class Model{
  153.                
  154.                 public void test1(){
  155.                         System.out.println("test1");
  156.                 }
  157.                 public void test2(Model a){
  158.                         System.out.println("test2");
  159.                 }
  160.                 public int test3(){
  161.                         System.out.println("test3");
  162.                         return 1;
  163.                 }
  164.         }
  165.        

  166.         Java 8 允許你使用 :: 關鍵字來引用構造函數
  167.         public class LambdaTest4 {
  168.                
  169.                 public static void main(String[] args) {
  170.                        
  171.                         //Lambda表達式引用構造函數
  172.                         //根據構造器的參數來自動匹配使用哪一個構造器
  173.                         Action4Creater creater = Action4::new;
  174.                         Action4 a4 = creater.create("zhangsan");
  175.                         a4.say();
  176.                        
  177.                        
  178.                 }
  179.                
  180.         }

  181.         class Action4{
  182.                 private String name;
  183.                 public Action4() {
  184.                        
  185.                 }
  186.                 public Action4(String name) {
  187.                         this.name = name;
  188.                 }
  189.                 public void say(){
  190.                         System.out.println("name = "+name);
  191.                 }
  192.         }

  193.         interface Action4Creater{
  194.                 public Action4 create(String name);
  195.         }

  196.         2.4 lambda表達式中的變量訪問
  197.         public class LambdaTest5 {
  198.                 private static int j;
  199.                 private int k;
  200.                 public static void main(String[] args) {
  201.                         LambdaTest5 t = new LambdaTest5();
  202.                         t.test();
  203.                 }
  204.                
  205.                 public void test(){
  206.                         int num = 10;
  207.                         j = 20;
  208.                         k = 30;
  209.                        
  210.                         //lambda表達式中可以訪問成員變量也可以方法局部變量
  211.                         Action5 a5 = (i)->System.out.println("操作后:i="+(i+num+j+k));
  212.                         a5.run(1);
  213.                        
  214.                         //但是這個被訪問的變量默認變為final修飾的 不可再改變 否則編譯不通過
  215.                         //num = 60;
  216.                         j = 50;
  217.                         k = 70;
  218.                 }
  219.                
  220.         }

  221.         interface Action5{
  222.                 public void run(int i);
  223.         }
  224.        
  225.        
  226.         2.5 Predicate接口和lambda表達式
  227.         java.util.function.Predicate接口是用來支持java函數式編程新增的一個接口,使用這個接口和lamb表達式就可以以更少的代碼為API方法添加更多的動態行為。
  228.         public class LambdaTest6 {
  229.                 public static void main(String[] args) {
  230.                         List<String> languages = Arrays.asList("Java", "html5","JavaScript", "C++", "hibernate", "PHP");
  231.                        
  232.                         //開頭是J的語言
  233.                         filter(languages,(name)->name.startsWith("J"));
  234.                         //5結尾的
  235.                         filter(languages,(name)->name.endsWith("5"));
  236.                         //所有的語言
  237.                         filter(languages,(name)->true);
  238.                         //一個都不顯示
  239.                         filter(languages,(name)->false);
  240.                         //顯示名字長度大于4
  241.                         filter(languages,(name)->name.length()>4);
  242.                         System.out.println("-----------------------");
  243.                         //名字以J開頭并且長度大于4的
  244.                         Predicate<String> c1 = (name)->name.startsWith("J");
  245.                         Predicate<String> c2 = (name)->name.length()>4;
  246.                         filter(languages,c1.and(c2));
  247.                        
  248.                         //名字不是以J開頭
  249.                         Predicate<String> c3 = (name)->name.startsWith("J");
  250.                         filter(languages,c3.negate());
  251.                        
  252.                         //名字以J開頭或者長度小于4的
  253.                         Predicate<String> c4 = (name)->name.startsWith("J");
  254.                         Predicate<String> c5 = (name)->name.length()<4;
  255.                         filter(languages,c4.or(c5));
  256.                        
  257.                         //名字為Java的
  258.                         filter(languages,Predicate.isEqual("Java"));
  259.                        
  260.                         //判斷倆個字符串是否相等
  261.                         boolean test = Predicate.isEqual("hello").test("world");
  262.                         System.out.println(test);
  263.                 }
  264.                 public static void filter(List<String> languages, Predicate<String> condition) {  
  265.                         for(String name: languages) {  
  266.                                 if(condition.test(name)) {  
  267.                                         System.out.println(name + " ");  
  268.                                 }  
  269.                         }  
  270.                 }  
  271.                
  272.         }
  273.        
  274.         2.6 Function 接口
  275.                  Function有一個參數并且返回一個結果,并附帶了一些可以和其他函數組合的默認方法
  276.                  compose方法表示在某個方法之前執行
  277.                  andThen方法表示在某個方法之后執行
  278.                  注意:compose和andThen方法調用之后都會把對象自己本身返回,這可以方便鏈式編程
  279.                  default <V> Function<T,V> andThen(Function<? super R,? extends V> after) 返回一個先執行當前函數對象apply方法再執行after函數對象apply方法的函數對象。

  280.                 default <V> Function<T,V> compose(Function<? super V,? extends T> before)返回一個先執行before函數對象apply方法再執行當前函數對象apply方法的函數對象。

  281.                 static <T> Function<T,T> identity() 返回一個執行了apply()方法之后只會返回輸入參數的函數對象。
  282.         public interface Function<T, R> {

  283.                 R apply(T t);

  284.                 default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
  285.                         Objects.requireNonNull(before);
  286.                         return (V v) -> apply(before.apply(v));
  287.                 }

  288.                 default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
  289.                         Objects.requireNonNull(after);
  290.                         return (T t) -> after.apply(apply(t));
  291.                 }
  292.                
  293.                 //注意: t->t是(t)->t的簡寫
  294.                 //t->t是作為方法identity的返回值的,也就是Function類型對象
  295.                 //類似于這樣的寫法:Function<Object, Object> f = t->t;
  296.                 //那么f.apply("test") 返回字符串"test"
  297.                 //傳入什么則返回什么
  298.                 static <T> Function<T, T> identity() {
  299.                         return t -> t;
  300.                 }
  301.         }

  302.         例如:
  303.         public class LambdaTest7 {
  304.                 //靜態內部類
  305.                 private static class Student{
  306.                         private String name;
  307.                         public Student(String name){
  308.                                 this.name = name;
  309.                         }
  310.                         public String getName() {
  311.                                 return name;
  312.                         }
  313.                        
  314.                 }
  315.                 public static void main(String[] args) {
  316.                         /*用戶注冊輸入一個名字tom*/
  317.                         String name = "tom";
  318.                        
  319.                         /*使用用戶的輸入的名字創建一個對象*/
  320.                         Function<String, Student> f1 =(s)->new Student(s);
  321.                         //注意上面的代碼也可以寫出這樣,引用類中的構造器
  322.                         //Function<String, Student> f1 =Student::new;
  323.                         Student stu1 = f1.apply(name);
  324.                         System.out.println(stu1.getName());
  325.                        
  326.                         /*需求改變,使用name創建Student對象之前需要給name加一個前綴*/
  327.                         Function<String,String> before = (s)->"briup_"+s;
  328.                         //表示f1調用之前先執行before對象的方法,把before對象的方法返回結果作為f1對象方法的參數
  329.                         Student stu2 = f1.compose(before).apply(name);
  330.                         System.out.println(stu2.getName());
  331.                        
  332.                         /*獲得創建好的對象中的名字的長度*/
  333.                         Function<Student,Integer> after = (stu)->stu.getName().length();
  334.                         //before先調用方法,結果作為參數傳給f1來調用方法,結果再作為參數傳給after,結果就是我們接收的數據
  335.                         int len = f1.compose(before).andThen(after).apply(name);
  336.                         System.out.println(len);
  337.                        
  338.                 }
  339.                
  340.         }        



  341.         2.7 Supplier接口
  342.         Supplier接口返回一個任意范型的值,和Function接口不同的是該接口沒有任何參數
  343.         public interface Supplier<T> {
  344.                 T get();
  345.         }
  346.         例如:
  347.         public class LambdaTest8 {
  348.                 public static void main(String[] args) {
  349.                         //生成一個八位的隨機字符串
  350.                         Supplier<String> f = ()->{
  351.                                 String base = "abcdefghijklmnopqrstuvwxyz0123456789";     
  352.                                 Random random = new Random();     
  353.                                 StringBuffer sb = new StringBuffer();     
  354.                                 for (int i = 0; i < 8; i++) {  
  355.                                         //生成[0,base.length)之間的隨機數
  356.                                         int number = random.nextInt(base.length());     
  357.                                         sb.append(base.charAt(number));     
  358.                                 }     
  359.                                 return sb.toString();   
  360.                         };
  361.                         System.out.println(f.get());
  362.                 }
  363.                
  364.         }


  365.         2.8 Consumer接口
  366.         Consumer接口接收一個任意范型的值,和Function接口不同的是該接口沒有任何值
  367.         public interface Consumer<T> {

  368.                 void accept(T t);

  369.                 default Consumer<T> andThen(Consumer<? super T> after) {
  370.                         Objects.requireNonNull(after);
  371.                         return (T t) -> { accept(t); after.accept(t); };
  372.                 }
  373.         }
  374.         例如:
  375.         public class LambdaTest9 {
  376.                 //靜態內部類
  377.                 private static class Student{
  378.                         private String name;

  379.                         public String getName() {
  380.                                 return name;
  381.                         }

  382.                         public void setName(String name) {
  383.                                 this.name = name;
  384.                         }
  385.                 }
  386.                
  387.                 public static void main(String[] args) {
  388.                         Student s = new Student();
  389.                         s.setName("tom");
  390.                        
  391.                         Consumer<Student> c =
  392.                         stu->System.out.println("hello!"+stu.getName());
  393.                         c.accept(s);
  394.                        
  395.                 }
  396.                
  397.         }

  398.         總結:
  399.                 Function<T, R>  接口   R apply(T t);       有參數有返回值
  400.                 Supplier<T>       接口   T get();                  沒參數有返回值
  401.                 Consumer<T>    接口   void accept(T t); 有參數沒返回值

  402.                 另外需要注意的接口: 其用法和上面介紹的接口使用方式類同
  403.                 BinaryOperator<T>接口    T apply(T t, T t)  將兩個T作為輸入,返回一個T作為輸出
  404.                 BiFunction<T, U, R>接口  R apply(T t, U u)  將一個T和一個U輸入,返回一個R作為輸出
  405.                 BinaryOperator接口繼承了BiFunction接口
  406.                 public interface BinaryOperator<T> extends BiFunction<T,T,T>

  407.                 BiConsumer<T, U>接口  void accept(T t, U u) 將倆個參數傳入,沒有返回值

  408.                

  409.         2.9 Optional類
  410.         Optional 不是接口而是一個類,這是個用來防止NullPointerException異常的輔助類型
  411.         Optional 被定義為一個簡單的容器,其值可能是null或者不是null。
  412.         在Java8之前一般某個函數應該返回非空對象但是偶爾卻可能返回了null,而在Java 8中,不推薦你返回null而是返回Optional。
  413.         這是一個可以為null的容器對象。
  414.         如果值存在則isPresent()方法會返回true,調用get()方法會返回該對象。
  415.         public class Optotion {
  416.        
  417.         public static void main(String[] args) {
  418.                
  419.                 /*of方法 為非null的值創建一個Optional*/
  420.                 //of方法通過工廠方法創建Optional類。
  421.                 //需要注意的是,創建對象時傳入的參數不能為null。
  422.                 //如果傳入參數為null,則拋出NullPointerException 。
  423.                 Optional<String> op1 = Optional.of("hello");
  424.                
  425.                 /*ofNullable方法 為指定的值創建一個Optional,如果指定的值為null,則返回一個空的Optional。*/
  426.                 //ofNullable與of方法相似,唯一的區別是可以接受參數為null的情況
  427.                 Optional<String> op2 = Optional.ofNullable(null);
  428.                
  429.                 /*isPresent方法 如果值存在返回true,否則返回false。*/
  430.                 /*get方法 如果Optional有值則將其返回,否則拋出NoSuchElementException。*/
  431.                 if(op1.isPresent()){
  432.                         System.out.println(op1.get());
  433.                 }
  434.                 if(op2.isPresent()){
  435.                         System.out.println(op2.get());
  436.                 }
  437.                
  438.                 /*ifPresent方法 如果Optional實例有值則為其調用consumer,否則不做處理*/
  439.                 //consumer接口中的方法只有參數沒有返回值
  440.                 op1.ifPresent(str->System.out.println(str));
  441.                 op2.ifPresent(str->System.out.println(str));//這個不執行 因為op2里面的值是null
  442.                
  443.                
  444.                 /*orElse方法 如果有值則將其返回,否則返回指定的其它值。*/
  445.                 System.out.println(op1.orElse("如果op1中的值為null則返回這句話,否則返回這個值"));
  446.                 System.out.println(op2.orElse("如果op2中的值為null則返回這句話,否則返回這個值"));
  447.                
  448.                
  449.                 /*orElseGet方法 orElseGet與orElse方法類似,區別在于得到的默認值。orElse方法將傳入的字符串作為默認值,orElseGet方法可以接受Supplier接口的實現用來生成默認值。*/
  450.                 //Supplier接口中的方法沒有參數但是有返回值
  451.                 System.out.println(op1.orElseGet(()->"自己定義的返回值"));
  452.                 System.out.println(op2.orElseGet(()->"自己定義的返回值"));
  453.                
  454.                
  455.                 /*orElseThrow方法 如果有值則將其返回,否則拋出supplier接口創建的異常。*/
  456.                 //在orElseThrow中我們可以傳入一個lambda表達式或方法,如果值不存在來拋出異常。
  457.                 //orElseThrow方法的聲明如下 所有只能返回一個Throwable類型對象
  458.                 //public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X
  459.                 try {
  460.                         System.out.println(op1.orElseThrow(Exception::new));;
  461.                         //System.out.println(op2.orElseThrow(Exception::new));;這個會拋出異常
  462.                 } catch (Exception e) {
  463.                         e.printStackTrace();
  464.                 }
  465.                
  466.                
  467.                 /*map方法 如果有值,則對其執行調用mapper函數得到返回值。*/
  468.                 //返回值并且依然Optional包裹起來,其泛型和你返回值的類型一致
  469.                 //public<U> Optional<U> map(Function<? super T, ? extends U> mapper)
  470.                 Optional<Integer> map1 = op1.map(str->1);
  471.                 System.out.println(map1.get());
  472.                 Optional<Double> map2 = op2.map(str->1.2);
  473.                 System.out.println(map2.orElse(0.0));
  474.                
  475.                
  476.                 /*flatMap方法 如果有值,為其執行mapper函數返回Optional類型返回值,否則返回空Optional。*/
  477.                 //flatMap與map方法類似,區別在于flatMap中的mapper返回值必須是Optional。調用結束時,flatMap不會對結果用Optional封裝。
  478.                 //需要我們自己把返回值封裝為Optional
  479.                 //public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)
  480.                 System.out.println(op1.flatMap(str->Optional.of(str+"_briup")).get());
  481.                 //op1.flatMap(str->"");編譯出錯
  482.                
  483.                
  484.                 /*filter方法 如果有值并且滿足斷言條件返回包含該值的Optional,否則返回空Optional。*/
  485.                 //public Optional<T> filter(Predicate<? super T> predicate)
  486.                 op1 = op1.filter(str->str.length()<10);
  487.                 System.out.println(op1.orElse("值為null"));
  488.                 op1 = op1.filter(str->str.length()>10);
  489.                 System.out.println(op1.orElse("值為null"));
  490.         }

  491.         2.10 Stream 接口
  492.         java.util.Stream 表示能應用在一組元素上一次執行的操作序列。
  493.         Stream 操作分為中間操作或者最終操作兩種,最終操作返回一特定類型的計算結果,
  494.         而中間操作返回Stream本身,這樣你就可以將多個操作依次串起來(鏈式編程)。
  495.         Stream 的創建需要指定一個數據源,比如 java.util.Collection的子類,List或者Set, Map不支持。
  496.         Stream的操作可以串行執行或者并行執行。
  497.         Stream 作為 Java 8 的一大亮點,它與 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念。
  498.         Java 8 中的 Stream 是對集合(Collection)對象功能的增強,它專注于對集合對象進行各種非常便利、
  499.         高效的聚合操作(aggregate operation),或者大批量數據操作 (bulk data operation)。
  500.         Stream API 借助于同樣新出現的Lambda表達式,極大的提高編程效率和程序可讀性。
  501.         同時它提供串行和并行兩種模式進行匯聚操作

  502.         2.10.1 Stream對象的構建:
  503.         // 1.使用值構建
  504.         Stream<String> stream = Stream.of("a", "b", "c");
  505.         // 2. 使用數組構建
  506.         String[] strArray = new String[] {"a", "b", "c"};
  507.         Stream<String> stream = Stream.of(strArray);
  508.         Stream<String> stream = Arrays.stream(strArray);
  509.         // 3. 利用集合構建(不支持Map集合)
  510.         List<String> list = Arrays.asList(strArray);
  511.         stream = list.stream();

  512.         對于基本數值型,目前有三種對應的包裝類型 Stream:IntStream、LongStream、DoubleStream。
  513.         當然我們也可以用 Stream<Integer>、Stream<Long> 、Stream<Double>,但是 自動拆箱裝箱會很耗時,所以特別為這三種基本數值型提供了對應的 Stream。
  514.         Java 8 中還沒有提供其它基本類型數值的Stream

  515.         2.10.2 數值Stream的構建:
  516.         IntStream stream1 = IntStream.of(new int[]{1, 2, 3});
  517.         //[1,3)
  518.         IntStream stream2 = IntStream.range(1, 3);
  519.         //[1,3]
  520.         IntStream stream3 = IntStream.rangeClosed(1, 3);

  521.         2.10.3 Stream轉換為其它類型:
  522.         Stream<String> stream = Stream.of("hello","world","tom");
  523.         // 1. 轉換為Array
  524.         String[] strArray  = stream.toArray(String[]::new);
  525.         // 2. 轉換為Collection
  526.         List<String> list1 = stream.collect(Collectors.toList());
  527.         List<String> list2 = stream.collect(Collectors.toCollection(ArrayList::new));
  528.         Set<String> set3 = stream.collect(Collectors.toSet());
  529.         Set<String> set4 = stream.collect(Collectors.toCollection(HashSet::new));
  530.         // 3. 轉換為String
  531.         String str = stream.collect(Collectors.joining()).toString();

  532.         特別注意 : 一個 Stream 只可以使用一次,上面的代碼為了簡潔而重復使用了多次。
  533.         這個代碼直接運行會拋出異常的:
  534.         java.lang.IllegalStateException: stream has already been operated upon or closed


  535.         2.10.4 Stream操作
  536.         當把一個數據結構包裝成Stream后,就要開始對里面的元素進行各類操作了。常見的操作可以歸類如下。

  537.         Intermediate:中間操作
  538.         map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered

  539.         Terminal: 最終操作
  540.         forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator

  541.         Short-circuiting: 短路操作
  542.         anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit


  543.         map/flatMap映射 把 Stream中 的每一個元素,映射成另外一個元素。
  544.         例子:
  545.         轉換大寫
  546.         Stream<String> wordList = Stream.of("hello","world","tom");
  547.         List<String> output = wordList.
  548.                                          map(String::toUpperCase).
  549.                                           collect(Collectors.toList());
  550.           //也可以直接使用forEach循環輸出
  551.         wordList.map(String::toUpperCase).collect(Collectors.toList()).forEach(System.out::println);

  552.         例子:
  553.         計算平方數
  554.         List<Integer> nums = Arrays.asList(1, 2, 3, 4);
  555.         List<Integer> squareNums =
  556.                                 nums.stream().
  557.                                 map(n -> n * n).
  558.                                 collect(Collectors.toList());
  559.         map生成的是個1:1映射,每個輸入元素,都按照規則轉換成為另外一個元素。還有一些場景,是一對多映射關系的,這時需要 flatMap。
  560.         map和flatMap的方法聲明是不一樣的
  561.         <R> Stream<R>      map(Function<? super T, ? extends R> mapper);
  562.         <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

  563.         例子:
  564.         //stream1中的每個元素都是一個List集合對象
  565.         Stream<List<Integer>> stream1 = Stream.of(
  566.                                          Arrays.asList(1),
  567.                                          Arrays.asList(2, 3),
  568.                                          Arrays.asList(4, 5, 6)
  569.                                  );
  570.                                 Stream<Integer> stream2 = stream1.
  571.                                 flatMap((e) -> e.stream());
  572.                                
  573.         stream2.forEach(e->System.out.println(e));//輸出1 2 3 4 5 6
  574.         flatMap 把 stream1 中的層級結構扁平化,就是將最底層元素抽出來放到一起,最終新的 stream2 里面已經沒有 List 了,都是直接的數字。

  575.         例子:
  576.         Stream<String> stream1 = Stream.of("tom.Li","lucy.Liu");
  577.         //flatMap方法把stream1中的每一個字符串都用[.]分割成了倆個字符串
  578.         //最后返回了一個包含4個字符串的stream2
  579.         Stream<String> stream2 = stream1.flatMap(s->Stream.of(s.split("[.]")));
  580.         stream2.forEach(System.out::println);
  581.         輸出結果:
  582.                 tom
  583.                 Li
  584.                 lucy
  585.                 Liu



  586.         forEach 遍歷 接收一個 Lambda 表達式,然后在 Stream 的每一個元素上執行該表達式。
  587.         forEach 是 terminal 操作,執行完stream就不能再用了
  588.         例子:
  589.         List<String> list = Arrays.asList("test","hello","world","java","tom","C","javascript");
  590.         list.stream().forEach(System.out::println);


  591.         filter 過濾 對原始 Stream 進行某項測試,通過測試的元素被留下來生成一個新 Stream。
  592.         通過一個predicate接口來過濾并只保留符合條件的元素,該操作屬于中間操作,所以我們可以在過濾后的結果來應用其他Stream操作(比如forEach)。forEach需要一個函數來對過濾后的元素依次執行。forEach是一個最終操作,所以我們不能在forEach之后來執行其他Stream操作
  593.         例子:
  594.         List<String> list = Arrays.asList("test","hello","world","java","tom","C","javascript");
  595.         list.stream().filter(s->s.length()>4).forEach(System.out::println);
  596.         注意:System.out::println 這個是lambda表達式中對靜態方法的引用


  597.         peek 對每個元素執行操作并返回一個新的 Stream
  598.         注意:調用peek之后,一定要有一個最終操作
  599.         peek是一個intermediate 操作
  600.         例子:
  601.         List<String> list = Arrays.asList("one", "two", "three", "four");
  602.         List<String> list2 = list.stream()
  603.                                  .filter(e -> e.length() > 3)
  604.                                  .peek(e -> System.out.println("第一次符合條件的值為: " + e))
  605.                                  .filter(e->e.length()>4)
  606.                                  .peek(e -> System.out.println("第二次符合條件的值為: " + e))
  607.                                  .collect(Collectors.toList());
  608.         System.out.println(list2.size());//打印結果為 1
  609.         最后list2中就存放的篩選出來的元素


  610.         findFirst 總是返回 Stream 的第一個元素,或者空,返回值類型:Optional。
  611.         如果集中什么都沒有,那么list.stream().findFirst()返回一個Optional<String>對象,
  612.                 但是里面封裝的是一個null。
  613.         例子:
  614.         List<String> list = Arrays.asList("test","hello","world");
  615.         Optional<String> first = list.stream().findFirst();
  616.         System.out.println(first.orElse("值為null"));


  617.         sort 排序
  618.         排序是一個中間操作,返回的是排序好后的Stream。如果你不指定一個自定義的Comparator則會使用默認排序。
  619.         對 Stream 的排序通過 sorted 進行,它比數組的排序更強之處在于你可以首先對 Stream 進行各類 map、filter、limit、skip 甚至 distinct 來減少元素數量后,再排序,這能幫助程序明顯縮短執行時間。
  620.         例子:
  621.         List<String> list = Arrays.asList("test","hello","world","java","tom","C","javascript");
  622.         list.stream().sorted().filter(s->s.startsWith("j")).forEach(System.out::println);
  623.         //按照字符串的長短排序
  624.         list.stream().sorted((s1,s2)->s1.length()-s2.length()).forEach(System.out::println);
  625.         需要注意的是,排序只創建了一個排列好后的Stream,而不會影響原有的數據源,排序之后原數據list是不會被修改的:


  626.         Map 映射
  627.         中間操作map會將元素根據指定的Function接口來依次將元素轉成另外的對象,
  628.                 下面的示例展示了將字符串轉換為大寫字符串。
  629.                 你也可以通過map來講對象轉換成其他類型,
  630.                 map返回的Stream類型是根據你map傳遞進去的函數的返回值決定的。
  631.         例子:
  632.         List<String> list = Arrays.asList("test","hello","world","java","tom","C","javascript");
  633.         list.stream().map(s->s.toUpperCase()).forEach(System.out::println);


  634.         Match 匹配
  635.         Stream提供了多種匹配操作,允許檢測指定的Predicate是否匹配整個Stream。
  636.         所有的匹配操作都是最終操作,并返回一個boolean類型的值。
  637.         //所有元素匹配成功才返回true 否則返回false
  638.         例子:
  639.         List<String> list = Arrays.asList("test","hello","world","java","tom","C","javascript");
  640.         boolean allMatch = list.stream().allMatch((s)->s.startsWith("j"));
  641.         System.out.println(allMatch);

  642.         //任意一個匹配成功就返回true 否則返回false
  643.         例子:
  644.         List<String> list = Arrays.asList("test","hello","world","java","tom","C","javascript");
  645.         boolean anyMatch = list.stream().anyMatch((s)->s.startsWith("j"));
  646.         System.out.println(anyMatch);

  647.         //沒有一個匹配的就返回true 否則返回false
  648.         例子:
  649.         List<String> list = Arrays.asList("test","hello","world","java","tom","C","javascript");
  650.         boolean noneMatch = list.stream().noneMatch((s)->s.startsWith("j"));
  651.         System.out.println(noneMatch);


  652.         Count 計數
  653.         計數是一個最終操作,返回Stream中元素的個數,返回值類型是long。
  654.         例子:
  655.         List<String> list = Arrays.asList("test","hello","world","java","tom","C","javascript");
  656.         long count = list.stream().filter(s->s.startsWith("j")).count();
  657.         System.out.println(count);


  658.         Reduce 規約/合并
  659.         這是一個最終操作,允許通過指定的函數來將stream中的多個元素規約合并為一個元素.
  660.         它提供一個起始值(種子),然后依照運算規則(BinaryOperator),
  661.                 和前面 Stream 的第一個、第二個、第 n 個元素組合。Stream.reduce,
  662.                 常用的方法有average, sum, min, max, and count,返回單個的結果值,
  663.                 并且reduce操作每處理一個元素總是創建一個新值.
  664.                 從這個意義上說,字符串拼接、數值的 sum、min、max等都是特殊的 reduce。
  665.                 例如 Stream 的 sum 就相當于
  666.         IntStream integers = IntStream.range(1, 10);
  667.         Integer sum = integers.reduce(0, (a, b) -> a+b); 或
  668.         Integer sum = integers.reduce(0, Integer::sum);
  669.         也有沒有起始值的情況,這時會把 Stream 的前面兩個元素組合起來,返回的是 Optional。
  670.         OptionalInt min = integers.reduce((a, b) -> a<b?a:b);
  671.         // 字符串連接,concat = "ABCD"
  672.         String concat                  = Stream.of("A", "B", "C", "D").reduce("", String::concat);
  673.         Optional<String> opStr = Stream.of("A", "B", "C", "D").reduce(String::concat);

  674.         例子:
  675.         List<String> list = Arrays.asList("test","javap","hello","world","java","tom","C","javascript");
  676.         Optional<String> reduce = list.stream().sorted((s1,s2)->s2.length()-s1.length()).filter(s->s.startsWith("j")).map(s->s+"_briup").reduce((s1,s2)->s1+"|"+s2);
  677.         System.out.println(reduce.orElse("值為空"));//打印結果為: javascript_briup|javap_briup|java_briup
  678.         整個代碼有點長,可以換行看下:
  679.         Optional<String> reduce    =  list.stream()
  680.                                           .sorted((s1,s2)->s2.length()-s1.length())
  681.                                           .filter(s->s.startsWith("j"))
  682.                                           .map(s->s+"_briup")
  683.                                           .reduce((s1,s2)->s1+"|"+s2);
  684.                 1.先調用stream方法
  685.                 2.再排序,按照字符串的長度進行排序,長的在前短的再后
  686.                 3.再過濾,字符串必須是以字符'j'開頭的
  687.                 4.再進行映射,把每個字符串后面拼接上"_briup"
  688.                 5.再調用reduce進行合并數據,使用"|"連接字符串
  689.                 6.最后返回Optional<String>類型數據,處理好的字符串數據就封裝在這個對象中                                  



  690.         limit/skip
  691.         limit 返回 Stream 的前面 n 個元素;skip 則是跳過前 n 個元素只要后面的元素
  692.         例子:
  693.         List<String> list = Arrays.asList("test","javap","hello","world","java","tom","C","javascript");
  694.         list.stream().limit(5).forEach(System.out::println);
  695.         list.stream().skip(5).forEach(System.out::println);




  696.         min/max/distinct
  697.         例子:
  698.         找出字符文件中字符字符最長的一行
  699.         BufferedReader br = new BufferedReader(new FileReader("src/com/briup/test/a.txt"));
  700.         int maxLen = br.lines().
  701.                            mapToInt(String::length).
  702.                            max().
  703.                            getAsInt();

  704.         System.out.println(maxLen);         
  705.         注意:lines方法把文件中所有行都返回并且轉換為一個Stream<String>類型對象,因為每行讀出的String類型數據,同時String::length是使用方法引用的特殊方式(因為泛型的緣故),上面的筆記中已經介紹過了,max()方法執行后返回的時候OptionalInt類型對象,所以接著調用了getAsInt方法來獲得這次運行結果的int值

  706.         例子:
  707.         找出全文的單詞,轉小寫,去掉空字符,去除重復單詞并排序
  708.         BufferedReader br = new BufferedReader(new FileReader("src/com/briup/test4/day17.txt"));
  709.         br.lines().
  710.            flatMap(s->Stream.of(s.split(" "))).
  711.            filter(s->s.length()>0).
  712.            map(s->s.toLowerCase()).
  713.            distinct().
  714.            sorted().
  715.            forEach(System.out::println);
  716.        


  717.         Stream.generate
  718.         通過Supplier接口,可以自己來控制Stream的生成。這種情形通常用于隨機數、常量的 Stream,或者需要前后元素間維持著某種狀態信息的 Stream。把 Supplier 實例傳遞給 Stream.generate() 生成的 Stream,由于它是無限的,在管道中,必須利用limit之類的操作限制Stream大小。可以使用此方式制造出海量的測試數據
  719.         public static<T> Stream<T> generate(Supplier<T> s);
  720.         例子:
  721.         生成100個隨機數并由此創建出Stream實例
  722.         Stream.generate(()->(int)(Math.random()*100)).limit(100).forEach(System.out::println);
  723.                


  724.         Stream.iterate
  725.         iterate 跟 reduce 操作很像,接受一個種子值,和一個 UnaryOperator(假設是 f)。
  726.                 然后種子值成為 Stream 的第一個元素,f(seed) 為第二個,f(f(seed)) 第三個,
  727.                 f(f(f(seed))) 第四個,以此類推。
  728.         該方法的聲明為:
  729.         public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)

  730.         UnaryOperator接口繼承了Function接口:
  731.         public interface UnaryOperator<T> extends Function<T, T>
  732.         例子:
  733.         生成一個等差數列
  734.         Stream.iterate(0, n -> n + 3).
  735.                                 limit(10).
  736.                                 forEach(x -> System.out.print(x + " "));
  737.         打印結果:
  738.         0 3 6 9 12 15 18 21 24 27




  739.         Collectors
  740.         java.util.stream.Collectors 類的主要作用就是輔助進行各類有用的操作。
  741.         例如把Stream轉變輸出為 Collection,或者把 Stream 元素進行分組。
  742.         例子:
  743.         把Stream中的元素進行過濾然后再轉為List集合
  744.         List<String> list = Arrays.asList("test","hello","world","java","tom","C","javascript");
  745.         List<String> result = list.stream().filter(s->s.length()>4).collect(Collectors.toList());

  746.         //分組:按照字符串的長度分組
  747.         List<String> list = Arrays.asList("test","hello","world","java","tom","C","javascript");
  748.         //相同長度的字符串放到一個List集合中作為Map的value,字符串的長度作為Map的Key
  749.         Map<Integer, List<String>> collect = list.stream().collect(Collectors.groupingBy(String::length));
  750.         //注意下面寫法可能寫到s->s.length()的時候Eclipse里面有可能不會代碼提示,這個要看你先的是=號的哪一邊
  751.         //最終原因還是泛型的事情
  752.         Map<Integer, List<String>> collect = list.stream().collect(Collectors.groupingBy(s->s.length()));
  753.                
  754.         //分割:按照字符串是否包含java進行劃分  partitioning分割劃分的意思
  755.         Map<Boolean, List<String>> collect =
  756.                         list.stream().collect(Collectors.partitioningBy(s->s.indexOf("java")!=-1));
  757.         for(Boolean b:collect.keySet()){
  758.                 System.out.println(b+" : "+collect.get(b).size());
  759.         }


  760.         2.11 并行Streams
  761.         Stream有串行和并行兩種,串行Stream上的操作是在一個線程中依次完成,而并行Stream則是在多個線程上同時執行。
  762.         例子:
  763.         public class LambdaTest12 {
  764.        
  765.                 public static void main(String[] args) {
  766.                        
  767.                         //生成100萬個不同的字符串放到集合中
  768.                         int max = 1000000;
  769.                         List<String> values = new ArrayList<String>(max);
  770.                         for (int i = 0; i < max; i++) {
  771.                             UUID uuid = UUID.randomUUID();
  772.                             values.add(uuid.toString());
  773.                         }


  774.                         //1納秒*10^9=1秒
  775.                         long t0 = System.nanoTime();
  776.                         //串行stream
  777.                         long count = values.stream().sorted().count();
  778.                         //并行stream
  779.                         //long count = values.parallelStream().sorted().count();
  780.                         long t1 = System.nanoTime();

  781.                         long time = t1 - t0;
  782.                         System.out.println(count);
  783.                         System.out.println(time);
  784.                 }
  785.                
  786.         }

  787.         結論:對100萬個字符串進行排序和計數操作,串行和并行運算的用時差別還是很明顯的



  788.         2.12 Map集合
  789.         Map類型不支持stream,不過Map提供了一些新的有用的方法來處理一些日常任務。
  790.         Java8為Map新增的方法:

  791.         Object compute(Object key, BiFunction remappingFunction):該方法使用remappingFunction根據原key-value對計算一個新的value。只要新的value不為null,就使用新的value覆蓋原value;如果新的value為null,則刪除原key-value對;

  792.         Object computeIfAbsent(Object key, Function mappingFunction):如果傳入的key參數在Map中對應的value為null,該方法將使用mappingFunction根據原key、value計算一個新的結果,則用該計算結果覆蓋原value;如果傳入的key參數在Map中對應的value為null,則該方法不做任何事情;如果原Map原來不包括該key,該方法可能會添加一組key-value對。

  793.         Object computeIfPresent(Object key, BiFunction remappingFunction):如果傳給該方法的key參數在Map中對應的value不為null,該方法將使用remappingFunction根據原key、value計算一個新結果,并且該計算結果不為null,則使用該結果覆蓋原來的value;如果計算結果為null,則刪除原key-value對。

  794.         void forEach(BiConsumer action):該方法是Java8為Map新增的一個遍歷key-value對的方法。

  795.         Object getOrDefault(Object key, V defaultValue):獲取指定的key對應的value。如果該key不存在,則返回defaultValue。

  796.         Object merge(Object key, Object value, BiFunction remappingFunction):該方法會先根據key參數獲取該Map中對應的value。如果獲取的value為null,則直接使用傳入的value覆蓋原value(在這種情況下,可能會添加一組key-value);如果獲取的value不為null,則使用remappingFunction函數根據原value、新value計算一個新的結果,并用新的結果去覆蓋原有的value。

  797.         Object putIfAbsent(Object key, Object value):該方法會自動檢測指定的key對應的value是否為null,如果該key對應的value為null,則使用傳入的新value代替原來的null。如果該key對應的value不是null,那么該方法不做任何事情。

  798.         Object replace(Object key, Object value):將Map中指定key對應的value替換成新value并把被替換掉的舊值返回。如果key在Map中不存在,該方法不會添加key-value對,而是返回null。

  799.         Boolean replace(K key, V oldValue, V newValue):將Map中指定的key-value對的原value替換成新value。如果在Map中找到指定的key-value對,則執行替換并返回true,否則返回false。

  800.         replaceAll(BiFunction function):該方法使用function對原key-value對執行計算,并將計算結果作為key-value對的value值
復制代碼




回復

使用道具 舉報