Java8

一、 HashMap 加入了紅黑樹html

二、ConcurrentHashMap  使用了CAS無鎖機制  java

三、 永久區沒有了,成了元空間(MetaSpace)。相對於方法區,直接使用的物理內存!相應的PremGenSiz 、 MaxPremGenSize 參數失效了 取而代之的是:MetaSpaceSize  MaxMetaSpaceSize 數組

四、Stream Api 和 Lambda表達式網絡

 

關於Lambda表達式:https://www.cnblogs.com/toov5/p/10583620.html多線程

 它是個匿名函數,能夠把它表達式理解爲是一段能夠傳遞的代碼(將代碼像數據同樣傳遞)app

 

//箭頭表達式   ->   左邊表示方法的參數列表 右邊表示實現 !框架

public interface MyInterface {
    public boolean bigger(T t);
} 

 

語法引伸: dom

  若是是沒有返回值的方法ide

  

 ()-> System.out.println("toov5");

 

 

其實Lambda就是簡化了 匿名函數  提取了關鍵代碼而已函數

對 Consumer接口中 Accept方法的實現

public class test111 {
    public static void main(String[] args) {
        Consumer<String> s = (x)-> System.out.println(x);  //有參數 無返回值的接口方法
        s.accept("toov5");
    }
}

若只有一個參數 小括號能夠省略:

 x-> System.out.println(x)

 有兩條參數而且多條語句:

public class test111 {
    public static void main(String[] args) {
        Comparator<Integer> com = (x, y) ->{
            System.out.println("toov5第一條語句");
            return Integer.compare(x, y);
        };
    }
}

 

若是隻有一條語句 大括號{} 以及return能夠省略

 

Lambda表達式的參數列表的數據類型能夠不寫,JVM編譯器能夠經過上下文推斷。   

 

總結:

  左右遇一括號省 

  左側推斷類型省

  

Lambda表達式須要 函數式接口的支持: 接口中只有一個抽象方法的接口。

 

 

改形成Lambda表達式:  就是函數式接口改爲  (參數) -> {實現}

定義一個函數式接口:

public interface myFun {
    Integer getValue(Integer x);
}

使用:

public class test {
    @Test
    public void test1(){
        Integer result = operation(10, x ->  x + 90 );
        System.out.println(result);
    }
    public Integer operation(Integer num, myFun myFun){
        return myFun.getValue(num);
    }
}

 

練習題1:

 調用Collection.sort() 方法,經過定製排序比價兩個Employee(先按照年齡比,年齡相同按照姓名比),使用Lambda做爲參數傳遞。

public class test22 {

        List<Employee> employees = Arrays.asList(
                new Employee(1, "11"),
                new Employee(2, "22"),
                new Employee(3, "33"),
                new Employee(4, "44"));
    @Test
     public void test11(){
        Collections.sort(employees , (e1,e2)->{
            if (e1.getNumber() == e2.getNumber()){
                return e1.getName().compareTo(e2.getName());
            }else
                return Integer.compare(e1.getNumber(), e2.getNumber());
        });
       for (Employee e : employees){
           System.out.println(e);
       }
     }
}

 

練習2:

  (1) 聲明函數式接口,接口中聲明抽象方法, public String getValue(String str)

    (2) 聲明類TestLambda, 類中編寫方法使用接口做爲參數,將一個字符串轉成大寫。並做爲方法的返回值

    (3) 再將一個字符串的第二個和第四個索引位置進行截取子串

 

像數據同樣傳遞:

函數式接口:

public interface myFun<T> {
    T getValue(T x);
}

 

使用:

    @Test
    public void test2(){
       String trimResult = strHalder("java is Toov5", s -> s.trim() );
        System.out.println(trimResult);
        String upperResult = strHalder("adsfsdf", s -> s.toUpperCase());
        System.out.println(upperResult);
        String subResult = strHalder("java is Toov5", s -> s.substring(2,5) );
        System.out.println(subResult);
    }
    public String strHalder(String str, myFun<String> myFun){
        return  myFun.getValue(str);
    }

運行結果:

 

 Lambda 想數據同樣 做爲參數傳遞

 函數式接口:

public interface myFun2<T,R> {
    public R getValue(T t1 , T t2);
}

使用:

 @Test
    public void test3(){
        op(100L, 200L, (x,y)->x+y);
        op(100L, 200L, (x,y)->x*y);
    }

    public void op(Long L1, Long L2 , myFun2<Long, Long> mf){
        System.out.println(mf.getValue(L1, L2));
    }

 

 

Java8 中內置的四大核心函數式接口


 

一、Consumer<T> 消費型接口   又去無會的   只有輸入沒有輸出

        void accept(T t)

 

    

舉栗子:

   @Test
    public void test4(){
        happy(1000, (m) -> System.out.println("消費了"));
    }

    public void happy(double money, Consumer<Double> consumer){
        consumer.accept(money);
    }

 

 

二、Supplier<T> 供給型接口   

      T get()     //沒參數 可是有返回值  調用就返回值

 

  舉個栗子:產生指定個數的整數,並放入集合中。 

     

        //每次纏身一個隨機數 一共十個
        List<Integer> numList = getNumList(10, () -> (int) (Math.random() * 100));
        for (Integer n : numList){
            System.out.println(n);
        }
    }

   public List<Integer> getNumList(int num, Supplier<Integer> supplier){
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < num; i++){
            Integer n = supplier.get();
            list.add(n);
        }
        return list;
   }
}

 

 

三、Function<T, R> 函數型接口  (傳入個T 返回個R)

      R apply(T t)

舉個栗子: 用於處理字符串

  @Test
   public void test6(){
       String result = setHandler("java is toov5", (str) -> str.toUpperCase());
       System.out.println(result);
   }

   public String setHandler(String str , Function<String, String> function){
        return function.apply(str);
   }

 

 

四、Predicate<T> 斷言型接口

    boolean test(T t)

 舉個栗子: 將知足條件的字符串添加到集合中去

    @Test
    public void test7(){
        List<String> list = Arrays.asList("hello", "java", "toov5", "go");
        List<String> resultList = filtefStr(list, (s) -> s.length() > 3);
        for (String s : resultList){
            System.out.println(s);
        }
    }
   public List<String> filtefStr(List<String> list , Predicate<String> predicate){
       ArrayList<String> strList = new ArrayList<>();
       for (String str : list){
           if (predicate.test(str)) {
               strList.add(str);
           }
       }
      return strList;
   }

 

   

小結:

  常常用的接口都給內置了

 要不咱們還得本身寫接口,寫這個接口函數

 

 還有好可能是這四個的子接口! 你們本身學習吧。

 

 

 

方法引用


 

 若Lambda體重的內容由方法已經實現了,可使用「方法引用」

  能夠當即爲方法引用是Lambda表達式的另一種表現形式

 

主要有三種語法格式:

    對象::實例方法名

 舉個栗子:

 

    @Test
    public void test8(){
        //Lambda體 方法、功能 已經有方法完成的狀況,可使用方法引用。 前提是: 參數列表 和 返回類型 一直    對象::實例
        Consumer<String> con = (x) -> System.out.println(x);

        PrintStream out = System.out; //表示實例
        Consumer<String> consumer = out::println;

        Consumer<String> consumer1 = System.out::println;
        consumer1.accept("toov5");
    }

 

 

    @Test
    public void test9(){
        Employee employee = new Employee(1, "toov5");
        Supplier<String> supplier = ()-> employee.getName();
        String str = supplier.get();
        System.out.println(str);

        Supplier<Integer> supplier1 = employee::getNumber;
        Integer result = supplier1.get();
        System.out.println(result);

    }

 

 

   類::靜態方法名

 

注意: 

 Lambda 體彙總嗲用方法的參數列表與返回值,要與函數式接口中抽象方法的函數列表和返回值類型保持一致。 能夠用方法引用

舉個栗子:

    @Test
    public void test10(){
        Comparator<Integer> reult =(x, y)-> Integer.compare(x,y);  //compare 是靜態方法 能夠經過類名去調用
        //等價於
        Comparator<Integer> result1 = Integer::compare;  //類:: 靜態方法名字
    }

 

    類:: 實例方法名

舉個栗子:

@FunctionalInterface
public interface BiPredicate<T, U> {

    /**
     * Evaluates this predicate on the given arguments.
     *
     * @param t the first input argument
     * @param u the second input argument
     * @return {@code true} if the input arguments match the predicate,
     * otherwise {@code false}
     */
    boolean test(T t, U u);

 

使用:

 

    @Test
    public void test11(){
        //實例方法名字!
        // 四大核心接口裏面沒有這個接口
        BiPredicate<String, String> bp = (x, y)-> x.equals(y);
        //上述在Lambda體中也是 一個方法搞定的 與此同時eqals仍是個實例方法
        //因而使用方法引用!  規則: 第一個參數是這個方法的調用者 第二個參數是要調用的方法的參數時候才能夠用實例方法引用
        BiPredicate<String, String> bp1 = String::equals;

    }

 

注意: Lambda參數列表中的第一個參數是實例方法的調用者,而第二個參數是實例方法的參數時候,可使用 ClassName:: method

 

 

2、 構造器引用:

  語法格式: 類名::new

  

 @Test
    public void test12(){
        Supplier<Employee> supplier = ()->new Employee();
        //上述經過構造器引用的方式
        Supplier<Employee> supplier1 = Employee::new;
        Employee employee = supplier1.get();
        System.out.println(employee);

    }

 

 能夠這麼玩兒:

 @Test
    public void test13(){
       Function<Integer, Employee> function = (x)-> new Employee(x);
      //能夠這麼玩兒  這個調用的是有參構造器 這個是取決於前面的 參數列表
        Function<Integer, Employee> function1 = Employee::new;
        Employee employee = function1.apply(100);
        System.out.println(employee);
    }

 

    @Test
    public void test14(){
       BiFunction<Integer, String, Employee> biFunction = Employee::new;
        Employee toov5 = biFunction.apply(12, "toov5");
        System.out.println(toov5);
    }

 

 注意: 須要調用的構造器的採納數列表要與函數式接口中抽象方法的參數列表保持一致! 構造器引用就能夠爲所欲爲的應用了!

 

 

3、數組的引用

語法: type[]:: new

    @Test
    public void test15(){
        Function<Integer, String[]> function = (x)-> new String[x];
        String[] arr = function.apply(10);  //傳入10 時候返回一個長度爲10的數組
        System.out.println(arr.length);
        Function<Integer,String[]> function1 = String[]::new;
        String[] arr1 = function1.apply(20);
        System.out.println(arr1.length);
    }

 

 

 

 牛逼的Stream APi


 

如同玩SQL同樣 ,操做Java的數據

 只要是流 都得有個數據源: 

    集合 、數組

 

相似於數據的傳輸,建立一個傳輸數據的管道,在傳輸過程當中,作一些列流水線式的中間操做。過濾、篩選、切片等

而後產生新的流。

對於原來的數據源不會有改變,數據源不會受到影響。

 

 至關於 複製了一份數據  改變的新的數據

 

 

 Stream 是數據渠道, 用於操做數據源(集合、數組等)所生成的元素序列。

 集合講的是數據,流講的是計算

 

 Stream的流程:  建立Stream  中間操做 終止操做

 

 建立stream的方式:

    能夠經過Collection 系列結合提供的 Stream()  或者  parallelStream()    注意前者獲取的是串行流 後者是並行流

   

    @Test
    public void test16(){
      List<String> list = new ArrayList<>();
      //方式1
        Stream<String> stringStream = list.stream(); //流也帶泛型的
       //方式2 經過Arrays中的靜態方法stream() 獲取數組流
        Employee[] employeesArr = new Employee[10];
        Stream<Employee> stream = Arrays.stream(employeesArr);
        //方式3:Stream類中的靜態方法
        Stream<String > stringStream1 = Stream.of("a","b","c"); //也能夠傳進數組
        //方式4: 建立無限流
         //1) 迭代  這裏的種子其實就是個起始值   配合終止操做
        Stream.iterate(0, x -> x + 2).limit(30)
                .forEach(System.out::println);
        //2) 生成
        Stream.generate(()-> Math.random()).limit(2)
                .forEach(System.out::println);
    }

 

 有了Strem 之後就能夠進行一系列流水線的中間操做了!

篩選、切片等

 

內容:

 篩選與切片  接受 Lambda  從流中排除某些元素

  limit 截斷流 是其元素不超過給定數量

  skip  跳過元素  返回一個扔掉了錢n個元素的流,元素不足返回空流  與limit(n) 互補 

  distinct 篩選 經過流所生成元元素的hashCode() 和 equals() 去除重複元素

 

 

沒有終止操做時候:

    @Test
    public void test17(){
     //只要有用到流 就會有那三步
        List<Employee> employees = Arrays.asList(
                new Employee(1, "11"),
                new Employee( 2, "22"),
                new Employee(3, "33"),
                new Employee(4, "44"));
        //先來個流
        Stream<Employee> employeeStream = employees.stream().filter((e) ->
                {
                    System.out.println("中間操做");
                   return e.getNumber() > 2;
                });
//        employeeStream.forEach(System.out::println);
    }

什麼都沒有打印

 

 加入終止條件:

    @Test
    public void test17(){
     //只要有用到流 就會有那三步
        List<Employee> employees = Arrays.asList(
                new Employee(1, "11"),
                new Employee( 2, "22"),
                new Employee(3, "33"),
                new Employee(4, "44"));
        //先來個流
        Stream<Employee> employeeStream = employees.stream().filter((e) ->
                {
                    System.out.println("中間操做");
                   return e.getNumber() > 2;
                });
        employeeStream.forEach(System.out::println);
    }

結果打印了,知足條件的都有打印::

 

 

過程當中看下 filter 所須要的參數類型:

 

 小結:

中間操做不會執行任何操做,只有當執行了終止操做後,中間操做一次性執行完成。「」惰性求值「」

 官方語言:

多箇中間操做能夠鏈接起來造成一個流水線,除非流水線上觸發終止操做。不然中間操做不會執行任何的處理。而在終止操做時候一次所有處理

 

 注意內部上述迭代是用Stream APi完成。

 

 內部迭代這麼玩兒:

  

 public void test(){
        List<Employee> employees = Arrays.asList(
                new Employee(1, "11"),
                new Employee( 2, "22"),
                new Employee(3, "33"),
                new Employee(4, "44"));

        Iterator<Employee> iterator = employees.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

 

 filter 而後 limit (2) 若是發現 兩條知足的數據後 後面的就不執行額 提升效率

 

    @Test
    public void test18() {
        List<Employee> employees = Arrays.asList(
                new Employee(1, "11"),
                new Employee(2, "22"),
                new Employee(3, "33"),
                new Employee(4, "44"));

        employees.stream().filter(
                e ->
                {
                    System.out.println("只是執行知足的哦");
                    return e.getNumber() > 2;
                }
        ).forEach(
                System.out::println
        );
    }

 

這個必定要注意的問題!

    @Test
    public void test19() {
        List<Employee> employees = Arrays.asList(
                new Employee(1, "11"),
                new Employee(2, "22"),
                new Employee(3, "33"),
                new Employee(4, "44"));

        employees.stream().filter(
                e -> e.getNumber() > 2
                ).skip(2)
                .distinct()  //若是想去除重複元素 對象必須重寫hashcode 和equals方法!
                .forEach(
                System.out::println
        );
    }

 

關於Stream的映射

  map-接受Lambda,將元素轉換成其餘形式或提取信息。接收一個函數做爲參數,該函數會被映射到每一個元素上,並將其映射成一個新的元素。

  flatMap 接受一個函數做爲參數,將流中的每一個值都轉換成一個流,而後把全部流鏈接成一個流

    @Test
    public void test20(){
         List<String> list = Arrays.asList("aaa","bbb","ccc","ddd");
         list.stream().map( str ->str.toUpperCase()).forEach(System.out::println);

        List<Employee> employees = Arrays.asList(
                new Employee(1, "11"),
                new Employee(2, "22"),
                new Employee(3, "33"),
                new Employee(4, "44"));
        employees.stream().map(Employee::getNumber).map(x->x+1)
                .forEach(System.out::println);
    }

 

 

下面的栗子:

 作個返回流的方法:

  map返回的是流

  流中還有一個流

 

    @Test
    public void test21(){
        List<String> list = Arrays.asList("aaa","bbb","ccc","ddd");
        // map自己返回流 裏面的函數又是返回流   流中有流  流中存放的是Character
        Stream<Stream<Character>> streamStream = list.stream().map(test::filterCharacter);
        streamStream.forEach( (sm) ->{
            sm.forEach(System.out::print);
        });

    }
    public static Stream<Character> filterCharacter(String str){
        List<Character> list = new ArrayList<>();
        for (Character ch : str.toCharArray()){
            list.add(ch);
        }
        return list.stream();
    }

 

 這樣就用到flatMap了   接收一個函數做爲參數,將流中的每一個值都換成一個流,而後把全部流鏈接成一個流

 

 與下面的這個進行比較:

 public static Stream<Character> filterCharacter(String str){
        List<Character> list = new ArrayList<>();
        for (Character ch : str.toCharArray()){
            list.add(ch);
        }
        return list.stream();
    }
    @Test
    public void test22(){
        List<String> list = Arrays.asList("aaa","bbb","ccc","ddd");
        Stream<Character> characterStream = list.stream().flatMap(test::filterCharacter);
        characterStream.forEach(System.out::println);
    }

 

 

 map至關於 每一個元素 「」aaa」  "bbb" 都是一個流 放入集合中。每一個都是一個小流

 flatmap 作成同一個流

 

聯想集合的操做: 

 相似於

           add( )   添加一個集合對象到集合中

           addAll( ) 添加一個集合的全部對象到集合中

 

 

 

關於Stream排序:

  排序 sorted() 天然排序(Comparable)

  sorted(Comparator com)  定製排序(Comparator)  //按照指定的方式進行排序

 

 @Test
    public void test23(){
        List<String> list = Arrays.asList("aaa","bbb","ccc","ddd");
        list.stream()
                .sorted()
                .forEach(System.out::println);

        List<Employee> employees = Arrays.asList(
                new Employee(1, "11"),
                new Employee(2, "22"),
                new Employee(3, "33"),
                new Employee(4, "44"));
        employees.stream().sorted( (e1, e2) -> {
            if (e1.getNumber().equals(e2.getNumber())){
                return e1.getName().compareTo(e2.getName());
            }else {
                return e1.getNumber().compareTo(e2.getNumber());
            }
        }).forEach(System.out::println);
    }

 

 

 關於Stream的終止操做:

    查找與匹配:

   allMatch 檢查是否匹配全部元素

   anyMatch 檢查是否至少匹配一個元素

   noneMatch 檢查是否沒有匹配全部元素

   findFirst 返回第一個元素

   findAny 返回當前流中的任意元素

  count  返回流中元素的總個數

   max返回流中的最大值

  min 返回流中的最小值

 

  @Test
public void test24(){
    List<Employee> employees = Arrays.asList(
            new Employee(1, "11",12.44,Employee.Status.BUSY),
            new Employee(2, "22",22.53,Employee.Status.FREE),
            new Employee(3, "33",64.3,Employee.Status.VOCATION),
            new Employee(4, "44",74.3,Employee.Status.BUSY));
    //是否是匹配全部元素呢
    boolean b1 = employees.stream().allMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
    System.out.println(b1);
    //是否至少匹配一個元素
    boolean b2 = employees.stream().anyMatch(employee -> employee.getStatus().equals(Employee.Status.BUSY));
    System.out.println(b2);
    //是否是沒有匹配全部元素
    boolean b3 = employees.stream().noneMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
    System.out.println(b3);
    //獲得第一個
    //補充:Java8 儘量避免空指針異常   就搞了個Optional容器類,把對象放在這個容器類中。
    // 若是容器中那個對象爲空 就找個替代的對象 op.orElse(obj)  最終的findFirst有可能爲空時候
    Optional<Employee> first = employees.stream()
            .sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
            .findFirst();
    System.out.println(first.get());

    //找一個空閒狀態的人 加入到我幹活的團隊中
        Optional<Employee> any = employees.stream()
                .filter(employee -> employee.getStatus()
                        .equals(Employee.Status.FREE)).findAny();
        System.out.println(any.get());

        //若是空閒的有兩個 能夠隨便   能夠經過並行流進行操做  多個線程同時找空閒的 誰找到算誰的
        //並行的話 獲取到誰就不必定了
        employees.parallelStream()
                .filter(employee -> employee.getStatus()
                        .equals(employee.getStatus()))
                .findAny();
       //count
        long count = employees.stream().count();
        System.out.println(count);
        //max  按照工資排序獲取員工工資最高的員工信息
        Optional<Employee> max = employees.stream().max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(max);
        //獲取最低工資
        Optional<Double> min = employees.stream().map(Employee::getSalary).min(Double::compare);
        System.out.println(max.get());
    }

 

 

 

 Stream 規約與收集

  reduce (T identity, BinaryOperator) / reduce(BinaryOperator)  能夠將流總元素反覆結合起來,獲得一個值

 

 注意reduce有好幾種:

 

 

    @Test
    public void test25(){
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
        //reduce 有好幾種 第一種有兩個參數的: 起始值, 二元運算。
        Integer result = list.stream().reduce(0, (x, y) -> x + y);
        System.out.println(result);
    }

reduce: 先把0 做爲起始值x 而後從流中取出一個元素y 。實現x+y  結果做爲x 而後繼續從流中取值做爲y 依次往下執行。 最終獲得一個新值

 

舉個栗子: 

累計公司中工資的總數:

  

    @Test
    public void test26(){
        List<Employee> employees = Arrays.asList(
                new Employee(1, "11",12.44,Employee.Status.BUSY),
                new Employee(2, "22",22.53,Employee.Status.FREE),
                new Employee(3, "33",64.3,Employee.Status.VOCATION),
                new Employee(4, "44",74.3,Employee.Status.BUSY));
        //注意返回Optional
        Optional<Double> result = employees.stream().map(Employee::getSalary)
                .reduce(Double::sum);//注意sum是Double的靜態方法
        System.out.println(result.get());
    }

運行結果:

 

 

注意第一個不是Optional的,由於不論如何不可能爲空,有個起始值的。有可能爲空的值是封裝到Optional中去

 

補充: map 和 reduce 的鏈接一般稱爲map-reduce,因谷歌用它進行網絡搜索出名

 

Stream的終止操做:

  count()     返回流中元素總數

  max(Comparator c )  返回流中最大值

  min( Comparator c) 返回流中最小值

  forEach(Consumer c)  內部迭代(使用Collection接口須要要好過戶去作迭代,稱爲外部迭代。相反,Stream API使用內部迭代-它幫你把迭代作了)  

 

規約:

 reduce(T iden, BinaryOperator b)  能夠將流匯中元素反覆結合起來,獲得一個值,返回T

 reduce(BinaryOperator b)    能夠將流中元素反覆結合起來,獲得一個值,返回 Option<T>

 

 

關於收集:

  collect 將流轉換爲其餘形式。 接收一個Collector接口的實現,用於給Stream中元素作彙總的方法

 

 collect(Collector c) 方法      將流轉換爲其餘形式。接收一個Collector接口的實現,用於給Stream中元素作彙總的方法

Collector接口中方法的實現決定了如何對劉執行收集操做(如手街道List 、Set、Map)。可是Cllectors實用類提供了不少靜態方法,能夠方便的建立常見收集器實例。具體方法實例

    @Test
    public void test27(){
        List<Employee> employees = Arrays.asList(
                new Employee(1, "11",12.44,Employee.Status.BUSY),
                new Employee(2, "22",22.53,Employee.Status.FREE),
                new Employee(3, "33",64.3,Employee.Status.VOCATION),
                new Employee(4, "44",74.3,Employee.Status.BUSY));
        List<Integer> list = employees.stream().map(Employee::getNumber).collect(Collectors.toList());
       list.forEach(System.out::println);
    }

 

若是有重複數據,放入到set中! 

   Set<Integer> list = employees.stream().map(Employee::getNumber).collect(Collectors.toSet());

 

 能夠這麼玩兒

    @Test
    public void test27(){
        List<Employee> employees = Arrays.asList(
                new Employee(1, "11",12.44,Employee.Status.BUSY),
                new Employee(2, "22",22.53,Employee.Status.FREE),
                new Employee(3, "33",64.3,Employee.Status.VOCATION),
                new Employee(4, "44",74.3,Employee.Status.BUSY));
        HashSet<Integer> list = employees.stream().map(Employee::getNumber).collect(Collectors.toCollection(HashSet::new)); //或者LinkedHashSet
       list.forEach(System.out::println);
    }

 

 

 @Test
    public void test27(){
        List<Employee> employees = Arrays.asList(
                new Employee(1, "11",12.44,Employee.Status.BUSY),
                new Employee(2, "22",22.53,Employee.Status.FREE),
                new Employee(3, "33",64.3,Employee.Status.VOCATION),
                new Employee(4, "44",74.3,Employee.Status.BUSY));
        //收集總數
        Long count = employees.stream().collect(Collectors.counting());
        System.out.println(count);
        //獲取平均值
        Double avg = employees.stream().collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println(avg);
        //總和
        DoubleSummaryStatistics sum = employees.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println(sum);
        //最大值        employee 最大值
        Optional<Employee> max = employees.stream().collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
        System.out.println(max.get());
        //最小值 (最小工資)
        Optional<Double> min = employees.stream().map(Employee::getSalary).collect(Collectors.minBy(Double::compare));
        System.out.println(min.get());
    }

 

 

關於分組的玩兒法:

    @Test
    public void test28(){
        List<Employee> employees = Arrays.asList(
                new Employee(1, "11",12.44,Employee.Status.BUSY),
                new Employee(2, "22",22.53,Employee.Status.FREE),
                new Employee(3, "33",64.3,Employee.Status.VOCATION),
                new Employee(4, "44",74.3,Employee.Status.BUSY));
         Map<Employee.Status, List<Employee>> map  = employees.stream()
                 .collect(Collectors.groupingBy(Employee::getStatus)); //分組
        System.out.println(map);
    }

 

多級分組的玩兒法: 多列分組 好比:先按照部門分 而後部門同樣按照薪資分等等

 

 @Test
    public void test29(){
        List<Employee> employees = Arrays.asList(
                new Employee(1, "11",12.44,Employee.Status.BUSY),
                new Employee(2, "22",22.53,Employee.Status.FREE),
                new Employee(3, "33",64.3,Employee.Status.VOCATION),
                new Employee(4, "44",74.3,Employee.Status.BUSY));
        Map<Employee.Status, Map<String, List<Employee>>> map = employees.stream()
                .collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
                    if (((Employee) e).getNumber() <= 35) {
                        return "新來";
                    } else {
                        return "toov5";
                    }
                })));//分組
        System.out.println(map);
    }

 

 

 

分區栗子:true 一個分區 false 一個分區

    @Test
    public void test30(){
        List<Employee> employees = Arrays.asList(
                new Employee(1, "11",12.44,Employee.Status.BUSY),
                new Employee(2, "22",22.53,Employee.Status.FREE),
                new Employee(3, "33",64.3,Employee.Status.VOCATION),
                new Employee(4, "44",74.3,Employee.Status.BUSY));
        Map<Boolean, List<Employee>> result = employees.stream()
                .collect(Collectors.partitioningBy((e) -> e.getSalary() > 50));//按照工資去分區  true一個區  false一個區域
        System.out.println(result);
    }

 

 

也能夠這麼玩兒哦  分區方式的這樣玩兒法: summarizingXXX

 

 收集: 將流轉換爲其餘形式,接收一個Collecttor接口實現,用於給Stream中元素作彙總的方法

  @Test
    public void test30(){
        List<Employee> employees = Arrays.asList(
                new Employee(1, "11",12.44,Employee.Status.BUSY),
                new Employee(2, "22",22.53,Employee.Status.FREE),
                new Employee(3, "33",64.3,Employee.Status.VOCATION),
                new Employee(4, "44",74.3,Employee.Status.BUSY));
        DoubleSummaryStatistics result = employees.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println(result.getAverage());
        System.out.println(result.getCount());
        System.out.println(result.getMax());
        System.out.println(result.getMin());
        System.out.println(result.getSum());
    }

 

 

 

鏈接字符串的操做:

 

  @Test
    public void test30(){
        List<Employee> employees = Arrays.asList(
                new Employee(1, "11",12.44,Employee.Status.BUSY),
                new Employee(2, "22",22.53,Employee.Status.FREE),
                new Employee(3, "33",64.3,Employee.Status.VOCATION),
                new Employee(4, "44",74.3,Employee.Status.BUSY));
        String result = employees.stream().map(Employee::getName).collect(Collectors.joining("#"));
        System.out.println(result);
    }

 

 

 

 

Stream API練習  


 

  給定一個數字列表,如何返回一個由每一個數的平方構成的列表呢?

 

用map去作

  @Test
    public void test31(){
        Integer[] nums = new Integer[]{1,2,3,4,5,6};
        Arrays.stream(nums).map(x -> x * x).forEach(System.out::println);
        //使用reduce
    }

 

數一數流中有多少Employee

用reduce 和 map 去作:

    @Test
    public void test32(){
        List<Employee> employees = Arrays.asList(
                new Employee(1, "11",12.44,Employee.Status.BUSY),
                new Employee(2, "22",22.53,Employee.Status.FREE),
                new Employee(3, "33",64.3,Employee.Status.VOCATION),
                new Employee(4, "44",74.3,Employee.Status.BUSY));
        Optional<Integer> result = employees.stream().map(employee -> 1).reduce(Integer::sum);
        System.out.println(result.get());
    }

 

 

綜合練習:

 兩個應用到的類:

Trader類:

package com.toov5.Java8;

//交易員類
public class Trader {

    private String name;
    private String city;

    public Trader() {
    }

    public Trader(String name, String city) {
        this.name = name;
        this.city = city;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    @Override
    public String toString() {
        return "Trader [name=" + name + ", city=" + city + "]";
    }

}

Transaction類

package com.toov5.Java8;

public class Transaction {

    private Trader trader;
    private int year;
    private int value;

    public Transaction() {
    }

    public Transaction(Trader trader, int year, int value) {
        this.trader = trader;
        this.year = year;
        this.value = value;
    }

    public Trader getTrader() {
        return trader;
    }

    public void setTrader(Trader trader) {
        this.trader = trader;
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return "Transaction [trader=" + trader + ", year=" + year + ", value="
                + value + "]";
    }

}

測試:

 @Test
    public void test33(){

        //找出2011年發生的全部交易,並按交易額進行排序(從高到低)
        transactions.stream()
                .filter( (t)->t.getYear() == 2011)
                .sorted( (t1,t2)-> Integer.compare(t1.getValue(), t2.getValue()))
                .forEach(System.out::println);
        //交易員都在哪些不一樣的城市工做?
        transactions.stream()
                .map((t)-> t.getTrader().getCity())
                .distinct()  //去重
                .forEach(System.out::println);
        //查找來自北京的交易員,而且按照姓名排序
        transactions.stream().filter( (t) -> t.getTrader().getCity().equals("Beiing"))
                .map(Transaction::getTrader)   //映射關係 取出交易員
                .sorted((t1, t2)-> t1.getName().compareTo(t2.getName()))
                .distinct()
                .forEach(System.out::println);
      //返回全部交易員的姓名字符串,按字母排序
        transactions.stream()
                     .map( (t)-> t.getTrader().getName())  //取出名字來
                    .sorted()
                    .forEach(System.out::print);
        System.out.println("----------------------");
        String result = transactions.stream()
                .map((t) -> t.getTrader().getName())  //取出名字來
                .sorted()
                .reduce("*", String::concat);
        System.out.println(result);
        //這麼玩兒
        System.out.println("*********");
        transactions.stream()
                .map( t -> t.getTrader().getName())
                .flatMap(test::filterCharacterMethod)
                .sorted( (s1,s2) -> s1.compareToIgnoreCase(s2))
                .forEach(System.out::print);

        //看看有沒有交易員在北京工做
        System.out.println("是否有人在北京工做");
        boolean city = transactions.stream().anyMatch((t) -> t.getTrader().getCity().equals("Beiing"));
        System.out.println(city);

        //打印生活在北京的全部交易額
        System.out.println("生活在北京的全部交易額");
        Optional<Integer> sum = transactions.stream()
                .filter((e) -> e.getTrader().getCity().equals("Beiing"))
                .map(Transaction::getValue)
                .reduce(Integer::sum);
        System.out.println(sum.get());

        //全部交易中 最高的交易額是多少
        System.out.println("最高的交易額是多少");
        Optional<Integer> max = transactions.stream()
                .map((t) -> t.getValue())
                .max(Integer::compare);
        System.out.println(max.get());

        //找到交易額最小的交易
        System.out.println("交易額最小的交易");
        Optional<Transaction> min = transactions.stream()
                .min((t1, t2) -> Integer.compare(t1.getValue(), t2.getValue()));
        System.out.println(min.get());
    }
    public static Stream<String> filterCharacterMethod(String  str){
        List<String> list = new ArrayList<>();
        for (Character ch : str.toCharArray()){
            list.add(ch.toString());
        }
        return list.stream();
    }

測試結果:

 

 

 

 並行流


 

 關於並行流和順序流

 並行流就是把一個內容分紅多個數據塊,而且用不一樣的線程分別處理每一個數據塊的流

 

 Java8 中將並行進行了優化, 咱們能夠很容易的對數據進行並行操做。Stream API能夠經過聲明性的經過parallel() 與 sequential() 在並行流與順序流之間進行切換

  

補充Fork/Join框架

Fork/Join框架:就是在必要狀況下,將一個大任務,進行拆分(fork) 成若干個小任務(拆到不可再拆時候),再將一個個小人物運行結果進行join彙總

設置一個臨界值,到達臨界值就再也不分子任務了。分到不能分位置

而後把這一個個子任務放入CPU的線程隊列去執行。執行結束以後,每一個子任務都會產生響應的結果。得出對應的結果後,再把結果進行join,最終合併成一個結果。

 

 Fork/Join 框架與傳統線程池的區別

 

採用 「工做竊取」模式(work-stealing): 當執行新的任務時它能夠將其拆分分紅更小的任務執行,並將小任務加到線 程隊列中,而後再從一個隨機線程的隊列中偷一個並把它放在本身的隊列中。 相對於通常的線程池實現,fork/join框架的優點體如今對其中包含的任務的 處理方式上.在通常的線程池中,若是一個線程正在執行的任務因爲某些緣由 沒法繼續運行,那麼該線程會處於等待狀態.而在fork/join框架實現中,若是 某個子問題因爲等待另一個子問題的完成而沒法繼續運行.那麼處理該子 問題的線程會主動尋找其餘還沒有運行的子問題來執行.這種方式減小了線程 的等待時間,提升了性能

 

爲啥效率高呢?

  所謂多線程就是把線程分配到不一樣的cpu核上,也就是不一樣的線程上進行執行。

  以四核cpu爲例,若是傳統的線程,則經過多線程完成任務。  

  

若是是傳統線程,每一個任務有可能會阻塞。每一個線程啥時候執行cpu說了算。一旦某個線程發生了阻塞的狀況,會有後果: 在這個線程上的其餘線程就無法執行了。

 cpu沒有很好的利用好資源,影響效率

 

fork/join 

 把每一個小任務壓入對應的線程中 造成一個個的線程隊列。而後進行工做竊取模式。

 不會閒着,去別的線程隊列去偷任務。更能充分利用cpu資源。

 

舉個栗子:

這兩個的區別是一個有返回值,一個沒有。

選擇個有返回值的,返回累加和

框架:

package com.toov5.Java8;

import java.util.concurrent.RecursiveTask;

public class ForkJoinCalculate extends RecursiveTask<Long> {
    private long start;
    private long end;
    //臨界值
    private static final long THREASHOLD = 10000;

    public ForkJoinCalculate(long start, long end){
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
         long length = end - start;
         if (length <= THREASHOLD){
             long sum = 0;
             for (long i = start; i < end; i++){
                 sum+=i;
             }
             return sum;
         }else{
             long middle = (start+end) / 2;
             ForkJoinCalculate left  = new ForkJoinCalculate(start, middle);
             left.fork(); //拆分子任務 同時壓入線程隊列
             ForkJoinCalculate right = new ForkJoinCalculate(middle + 1, end);
             right.fork();
             return left.join() + right.join();
         }
    }
}

使用:

package com.toov5.Java8;

import org.junit.Test;

import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;

public class testForkJoin {

    //須要forkJoin線程池的支持
    @Test
    public void test1(){
        Instant start = Instant.now();//計算時間戳

        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinCalculate(0, 100000L);
        Long sumResult = pool.invoke(task);
        System.out.println(sumResult);

        Instant end = Instant.now();
        System.out.println(Duration.between(start, end).toMillis());  //gtNano 納秒 toMillis()毫秒
    }
    @Test
    public void test2(){

        Instant start = Instant.now();

        Long sum = 0L;
        for (long i = 0; i< 100000L; i++){
            sum+=i;
        }
        System.out.println(sum);
        Instant end = Instant.now();
        System.out.println("耗費時間"+ Duration.between(start, end).toMillis());
    }
}

 

注意: 數值小的話,拆分是須要花時間的。不要隨便用ForkJoin框架,數值大才適合這樣玩兒。

           臨界值的設置也須要注意合適哦

 

以上的ForkJoin 比較繁瑣,jdk8作了優化:

底層幫助實現了fork / join

 

java 8 並行流:

@Test
    public void test3(){
        //順序流
        long result1 = LongStream.rangeClosed(0, 100000L)
                .reduce(0, Long::sum);
        //並行流
        Instant start = Instant.now();
        LongStream.rangeClosed(0, 100000L)
                .parallel()   //能夠與sequential() 在並行流和順序流之間進行切換
                .reduce(0, Long::sum);
        Instant end = Instant.now();
        System.out.println("耗費時間"+ Duration.between(start, end).toMillis());

    }

 

 

 

 

關於Optional容器類


 

 OPtional<T> 類(Java.util.Optional) 是一個容器類,帶包一個值存在或不存在,原來用null表示一個值不存在,如今Optional能夠更好的表達這個概念,而且能夠避免空指針異常。

經常使用方法:

 1.Optinal.of(T t)   建立一個Optinal實例

     

  @Test
     public void test3(){
      //建立實例 封裝對象 封裝employee
         Optional<Employee> op = Optional.of(new Employee());
         Employee employee = op.get();
         System.out.println(employee);
     }

 

若是不是咱們本身new的,是從別的地方傳遞過來的呢?

  @Test
     public void test3(){
      //建立實例 封裝對象 封裝employee
         Optional<Employee> op = Optional.of(null);
         Employee employee = op.get();
         System.out.println(employee);
     }

 

 

 2.Optional.empty()  建立一個空的Optional實例

 

    @Test
    public void test4(){
        //建立實例 封裝對象 封裝employee
        Optional<Employee> op = Optional.empty();
        System.out.println(op.get());  //空指針是發生在這裏的 

    }

 

 3.Optional.ofNullable(T t)  若t不爲null。 建立Optional實例,不然建立空實例

    @Test
    public void test5(){
        //建立實例 封裝對象 封裝employee
        Optional<Employee> op = Optional.ofNullable(new Employee());
        System.out.println(op.get());  //空指針是發生在這裏的

    }

構建了一個空對象

 

 

 4.isPresent() 判斷是否包含值

 

    @Test
    public void test6(){
        Optional<Employee> op = Optional.ofNullable(new Employee());
        if (op.isPresent()){  //若是裏面有值 就get
            System.out.println(op.get());
        }
    }

 

5. orElse(T t) 若是調用對象包含值,返回該值,不然返回t

  

   @Test
    public void test6(){
        Optional<Employee> op = Optional.ofNullable(null);
        //若是沒有值就獲取下面的  沒有值就用下面的代替 Default
        Employee toov5 = op.orElse(new Employee(16, "toov5", 34.34, Employee.Status.FREE));
        System.out.println(toov5);

    }

對於空的判斷放到了容器中了

 

 6.orElseGt(Supplier s) 若是調用對象包含之,返回該值,不然返回s獲取的值

 

    @Test
    public void test7(){
        Optional<Employee> op = Optional.ofNullable(null);
        //若是沒有值就獲取下面的  沒有值就用下面的代替 Default
        Employee employee = op.orElseGet(() -> new Employee());  //這裏能夠lambda表達式函數式接口 天馬行空 的功能
        System.out.println(employee);
    }

 

7. map(Function f ) 若是有值對其處理,並返回處理後的Optional,不然返回Optional.empty()

    @Test
    public void test8(){
        Optional<Employee> toov5 = Optional.ofNullable(new Employee(16, "toov5", 34.34, Employee.Status.FREE));
        Optional<String> s = toov5.map((e) -> e.getName());  //把容器裏面的對象應用到Map函數上了
        System.out.println(s);
    }

 

8. flatMap(Function mapper) 與map相似,要求返回值必須是Optional

 

  @Test
    public void test10(){
        Optional<Employee> toov5 = Optional.ofNullable(new Employee(16, "toov5", 34.34, Employee.Status.FREE));
        //必須包裝到Optional中
        Optional<String> s = toov5.flatMap((t) -> Optional.of(t.getName()));
        System.out.println(s.get());
    }

這樣進一步防止空指針異常

 

 

 案例:

man類:

 

    @Test
    public void test34(){
        //獲取一個男人心中女神的名字
        Man man = new Man();
        String godnessName = getGodnessName(man);
        System.out.println(godnessName);
    }
   public String getGodnessName(Man man){
        return man.getGoddness().getName();
   }

 man沒有女神 因而發生了空指針異常

 

 

 因而乎能夠這麼解決:

    @Test
    public void test34(){
        //獲取一個男人心中女神的名字
        Man man = new Man();
        String godnessName = getGodnessName(man);
        System.out.println(godnessName);
    }
   public String getGodnessName(Man man){
       if (man != null){
           Goddness goddness = man.getGoddness();
           if (goddness != null){
               return goddness.getName();
           }
       }
       return "java";
   }

上述容易if嵌套容易嵌套的很深! 好比男神心中有個男神女神心中有個渣男渣男心中有個外遇

 

 java8 能夠更好的解決~  Optional容器類

  

 獲取一個男人心中女神的名字

 

  @Test
   public void test35(){
       String godnessName = getGodnessName(null);
       System.out.println(godnessName);
   }
    public static String getGodnessName2(Optional<NewMan> man){
        //傳進來的對象能夠用 沒有的話就用默認的
        return man.orElse(new NewMan())
                .getGoddness()
                .orElse(new Goddness("toov5"))
                .getName();
    }

這樣:

新建NewMan類

public class NewMan {
    //有可能有 有可能沒有的值包裝在Optional類裏面
    private Optional<Goddness> goddness = Optional.empty(); //這樣賦值使用!

    public NewMan() {
    }
    public NewMan(Optional<Goddness> goddness) {
        this.goddness = goddness;
    }

    public Optional<Goddness> getGoddness() {
        return goddness;
    }

    public void setGoddness(Optional<Goddness> goddness) {
        this.goddness = goddness;
    }

    @Override
    public String toString() {
        return "NewMan{}";
    }
}

測試

 @Test
   public void test35(){
//       String godnessName = getGodnessName(null);
//       System.out.println(godnessName);
       Optional<NewMan> op = Optional.ofNullable(null);
       String str = getGodnessName2(op);
       System.out.println(str);
   }
    public static String getGodnessName2(Optional<NewMan> man){
        //傳進來的對象能夠用 沒有的話就用默認的
        return man.orElse(new NewMan())
                .getGoddness()
                .orElse(new Goddness("toov5"))
                .getName();
    }

或者:

   @Test
   public void test35(){
//       String godnessName = getGodnessName(null);
//       System.out.println(godnessName);
       Optional<NewMan> op = Optional.ofNullable(new NewMan());
       String str = getGodnessName2(op);
       System.out.println(str);
   }
    public static String getGodnessName2(Optional<NewMan> man){
        //傳進來的對象能夠用 沒有的話就用默認的
        return man.orElse(new NewMan())
                .getGoddness()
                .orElse(new Goddness("toov5"))
                .getName();
    }

 

 

 @Test
   public void test35(){
//       String godnessName = getGodnessName(null);
//       System.out.println(godnessName);
       Optional<Goddness> gn = Optional.ofNullable(null); 
       Optional<NewMan> op = Optional.ofNullable(new NewMan(gn));
       String str = getGodnessName2(op);
       System.out.println(str);
   }
    public static String getGodnessName2(Optional<NewMan> man){
        //傳進來的對象能夠用 沒有的話就用默認的
        return man.orElse(new NewMan())
                .getGoddness()
                .orElse(new Goddness("toov5"))
                .getName();
    }

 

接口中的默認方法與靜態方法


 

 Java8  中接口中有默認的實現方法

  

接口默認方法的 「類優先」 原則

   若一個接口彙總定義了一個默認方法,而另一個父類或接口中定義了一個同名的方法時

     選擇父類中的方法。若是一個父類提供了具體的實現,那麼接口中具備相同名稱和參數默認方法會被忽略

   接口衝突。若是一個父類接口提供一個默認方法,而另外一個接口也提供了一個具備相同名稱和參數列表的方法(無論方法是不是默認方法), 那麼必須覆蓋該方法來解決衝突

 

普通類:

public class MyClass {
    public String getName(){
        return "MyClass";
    }
}

 

接口類及其默認方法:

public interface MyFun {
    default String getName(){
        return "default";
    }
}

 

接口及其實現類:

public interface MyInterface {
    default String getName(){
        return "MyInterface";
    }
}

 

繼承父類的子類

public class SubClass implements MyFun, MyInterface{

    @Override
    public String getName() {
        return MyFun.super.getName();
    }
}

 

運行:

public class TestDefaultInterface {
    public static void main(String[] args) {
        SubClass sc = new SubClass();
        System.out.println(sc.getName());
    }
}

結果:

 

 Java8除了能夠有個默認方法,還能夠有靜態方法

 

public interface MyInterface {
    default String getName(){
        return "MyInterface";
    }
    //
    public static void show(){
        System.out.println("接口中的靜態方法");
    }
}

使用:

public class TestDefaultInterface {
    public static void main(String[] args) {
        MyInterface.show();
    }
}

 

 類和接口此時已經差距不大了,可是類能夠實現實現多接口,不能夠多繼承

 

public interface MyInterface {
    public static void show(){
        System.out.println("接口中的靜態方法");
    }
}

測試:

public class TestDefaultInterface {
    public static void main(String[] args) {
        MyInterface.show();
    }
}

結果:

相關文章
相關標籤/搜索