java8新特性學習2

6、Stream API 

Java8中有兩大最爲重要的改變。第一個是 Lambda 表達式;另一個則是 Stream API(java.util.stream.*)。Stream 是 Java8 中處理集合的關鍵抽象概念,它能夠指定你但願對集合進行的操做,能夠執行很是複雜的查找、過濾和映射數據等操做。使用Stream API 對集合數據進行操做,就相似於使用 SQL 執行的數據庫查詢。也可使用 Stream API 來並行執行操做。簡而言之,Stream API 提供了一種高效且易於使用的處理數據的方式java

流(Stream) 究竟是什麼呢?
是數據渠道,用於操做數據源(集合、數組等)所生成的元素序列。「集合講的是數據,流講的是計算!」
注意:
①Stream 本身不會存儲元素。
②Stream 不會改變源對象。相反,他們會返回一個持有結果的新Stream。
③Stream 操做是延遲執行的。這意味着他們會等到須要結果的時候才執行。(也就是中間操做不會當即執行)數據庫

 

Stream操做分爲三個步驟數組

 一、建立 Stream一個數據源(如:集合、數組),獲取一個流安全

     二、中間操做一箇中間操做鏈,對數據源的數據進行處理app

  三、終止操做(終端操做)一個終止操做,執行中間操做鏈,併產生結果框架

 

 步驟一:建立Stream的四種方式dom

package com.bjsxt.stream;

import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; /** * stream的三個步驟 * 一、建立stream * 二、中間操做 * 三、終止操做(終端操做) */ public class TestStream01 { public static void main(String[] args){ test01(); } /** * 建立stream的四種方式 */ public static void test01(){ // 一、能夠經過Collection系列集合提供的stream()或parallelStream() List<String> list=new ArrayList<String>(); Stream<String> stream=list.stream(); // 二、能夠經過Arrays中的靜態方法stream()獲取數據流 String[] strArr=new String[10]; Stream<String> stream2=Arrays.stream(strArr); // 三、經過Stream類中的靜態方法of() Stream<String> stream3=Stream.of("aa","bb","cc"); // 四、建立無限流 //方式一:迭代,使用Stream.iterate()方法 Stream<Integer> stream4=Stream.iterate(0,(x)->x+2); //方式二:生成,使用Stream.generate()方法 Stream<Double> stream5=Stream.generate(()->Math.random()); stream5.forEach(System.out::println); //這個forEach()方法是終端操做,打印用  } }

步驟二:中間操做ide

  多箇中間操做能夠鏈接起來造成一個流水線,除非流水線上觸發終止操做,不然中間操做不會執行任何的處理!而在終止操做時一次性所有處理,稱爲「惰性求值」。函數

  中間操做有不少種,主要有三種性能

  一、篩選與切片

package com.bjsxt.stream;

import com.bjsxt.Person; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; /** * stream * 中間操做 */ public class TestStream02 { public static List<Person> list= Arrays.asList( new Person(1,"段然濤",18), new Person(2,"李飛宇",34), new Person(3,"盧曉倩",25), new Person(4,"李小靜",67), new Person(4,"李小靜",67), new Person(4,"李小靜",67), new Person(5,"羅澤成",12) ); public static void main(String[] args){ } /** * filter()方法 */ public static void tesst01(){ Stream<Person> stream=list.stream(); //獲取流 stream.filter((per)->per.age>18) //filter()接收 Lambda , 從流中排除某些元素,會進行隱式迭代 .forEach(System.out::println); //這個是終端操做,這裏使用只是方便打印  } /** * limit()方法 */ public static void tesst02(){ Stream<Person> stream=list.stream(); //獲取流 stream.filter((per)->per.age>18) //filter()接收 Lambda , 從流中排除某些元素 .limit(2) //截斷流,使其元素不超過給定數量。也就是隻要篩選出來的前兩個,這裏操做相似於短路(&&),當篩選出來兩個後,後面的就再也不迭代,提升效率 .forEach(System.out::println); //這個是終端操做,這裏使用只是方便打印  } /** * skip()方法 */ public static void tesst03(){ Stream<Person> stream=list.stream(); //獲取流 stream.filter((per)->per.age>18) //filter()接收 Lambda , 從流中排除某些元素 .skip(2) //跳過前兩個元素,跟limit恰好相反 .forEach(System.out::println); //這個是終端操做,這裏使用只是方便打印  } /** *distinct()方法 */ public static void tesst04(){ Stream<Person> stream=list.stream(); //獲取流 stream.filter((per)->per.age>18) //filter()接收 Lambda , 從流中排除某些元素 .distinct() //篩選,經過流所生成元素的 hashCode() 和 equals() 去 除重複元素 .forEach(System.out::println); //這個是終端操做,這裏使用只是方便打印  } }

 

  二、映射

  

package com.bjsxt.stream;

import com.bjsxt.Person; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; /** * stream * 中間操做 */ public class TestStream03 { public static List<Person> list= Arrays.asList( new Person(1,"段然濤",18), new Person(2,"李飛宇",34), new Person(3,"盧曉倩",25), new Person(4,"李小靜",67), new Person(4,"李小靜",67), new Person(4,"李小靜",67), new Person(5,"羅澤成",12) ); public static void main(String[] args){ tesst02(); } /** * map()方法 */ public static void tesst01(){ Stream<Person> stream=list.stream(); //獲取流 stream.map((person -> person.getName())) //接收一個函數做爲參數,該函數會被應用到每一個元素上,並將其映射成一個新的元素  .forEach(System.out::println); System.out.println("-----------------------------------------------------------------------------"); List<String> list=Arrays.asList("aa","bb","cc"); list.stream().map(str->str.toUpperCase()) .forEach(System.out::println); } /** * flatMap()方法 */ public static void tesst02(){ List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee"); Stream<Stream<Character>> stream=strList.stream().map(TestStream03::splitString); stream.forEach(sm->sm.forEach(System.out::println)); //若是使用map須要這樣遍歷  System.out.println("------------------------------"); /** * map和flatMap的區別就如集合中add(集合)和addAll(集合)的區別 */ Stream<Character> stream2 = strList.stream() .flatMap(TestStream03::splitString); //接收一個函數做爲參數,將流中的每一個值都換成另外一個流,而後把全部流鏈接成一個流  stream2.forEach(System.out::println); } public static Stream<Character> splitString(String str){ List<Character> list=new ArrayList<Character>(); for(Character c:str.toCharArray()){ list.add(c); } return list.stream(); } }

  三、排序

package com.bjsxt.stream;

import com.bjsxt.Person; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; /** * stream * 中間操做 */ public class TestStream04 { public static List<Person> list= Arrays.asList( new Person(1,"段然濤",18), new Person(2,"李飛宇",34), new Person(3,"盧曉倩",25), new Person(4,"李小靜",67), new Person(4,"李小靜",67), new Person(4,"李小靜",67), new Person(5,"羅澤成",12) ); public static void main(String[] args){ tesst02(); } /** * sorted()方法,按照天然順序排序 */ public static void tesst01(){ List<String> list=Arrays.asList("bb","aa","dd","ee"); list.stream() .sorted() .forEach(System.out::println); } /** * sorted(Comparator<? super T> comparator)方法,須要傳一個比較器過去,按照指定的比較器進行排序 */ public static void tesst02(){ list.stream().sorted((e1,e2)->{ if(e1.age==e2.age){ return e1.name.compareTo(e2.name); }else{ return e1.age.compareTo(e2.age); } }).forEach(System.out::println); } }

 

步驟三:終止操做

終端操做會從流的流水線生成結果。其結果能夠是任何不是流的值,例如:List、Integer,甚至是 void,終止操做也有多種

  一、查找與匹配

  

package com.bjsxt.stream;

import com.bjsxt.Person; import java.util.Arrays; import java.util.List; import java.util.Optional; /** * stream * 終止操做 */ public class TestStream05 { public static List<Person> list= Arrays.asList( new Person(1,"段然濤",18), new Person(2,"李飛宇",34), new Person(3,"盧曉倩",25), new Person(4,"李小靜",67), new Person(4,"李小靜",67), new Person(4,"李小靜",67), new Person(5,"羅澤成",12) ); public static void main(String[] args){ tesst01(); } /** * 終止操做 * allMatch——檢查是否匹配全部元素 * anyMatch——檢查是否至少匹配一個元素 * noneMatch——檢查是否沒有匹配的元素 * findFirst——返回第一個元素 * findAny——返回當前流中的任意元素 * count——返回流中元素的總個數 * max——返回流中最大值 * min——返回流中最小值 */ public static void tesst01(){ boolean b1=list.stream().allMatch(person -> person.getAge()>10); //是否全部人的年齡大於10  System.out.println(b1); boolean b2=list.stream().anyMatch(person -> person.getAge()>100); //是否至少一我的的年齡大於100  System.out.println(b2); boolean b3=list.stream().noneMatch(person -> person.getAge()>100); //沒有一我的的年齡大於100  System.out.println(b3); Optional<Person> optional=list.stream() //Optional是一個容器。java8新出的,防止空指針異常 .findFirst(); //取第一個 Person person=optional.get(); System.out.println(person); Optional<Person> optiona2=list.stream() .findAny(); //取任何一個 Person person2=optional.get(); System.out.println(person2); Long ll=list.stream().count(); //個數  System.out.println(ll); Optional<Person> optiona3=list.stream().max((e1,e2)->{ // 取最大值,根據指定比較器 if(e1.age==e2.age){ return e1.name.compareTo(e2.name); }else{ return e1.age.compareTo(e2.age); } }); System.out.println(optiona3.get()); Optional<Person> optiona4=list.stream().min((e1,e2)->{ // 取最小值,根據指定比較器 if(e1.age==e2.age){ return e1.name.compareTo(e2.name); }else{ return e1.age.compareTo(e2.age); } }); System.out.println(optiona4.get()); } }

   二、歸約

package com.bjsxt.stream;

import com.bjsxt.Person; import java.util.*; import java.util.function.BinaryOperator; import java.util.stream.Collector; import java.util.stream.Collectors; /** * stream * 終止操做 */ public class TestStream06 { public static List<Person> list= Arrays.asList( new Person(1,"段然濤",18), new Person(2,"李飛宇",34), new Person(3,"盧曉倩",25), new Person(4,"李小靜",67), new Person(4,"李小靜",18), new Person(4,"李小靜",67), new Person(5,"羅澤成",12) ); public static void main(String[] args){ tesst01(); } /** *reduce()規約 能夠將流中元素反覆結合起來,獲得一個值。返回 T */ public static void tesst01(){ List<Integer> list1=Arrays.asList(1,2,3,4,5,6,7,8,9,10); Integer integer=list1.stream().reduce(0, (x,y)->x+y); //0是起始值。而後0+1+2+3+4+5+...  System.out.println(integer); System.out.println("---------------------------------------------"); Optional<Integer> optional=list.stream() .map(Person::getAge) .reduce(Integer::sum); System.out.println(optional.get()); } }

  三、收集

package com.bjsxt.stream;

import com.bjsxt.Person; import java.util.*; import java.util.function.BinaryOperator; import java.util.stream.Collector; import java.util.stream.Collectors; /** * stream * 終止操做 */ public class TestStream06 { public static List<Person> list= Arrays.asList( new Person(1,"段然濤",18), new Person(2,"李飛宇",34), new Person(3,"盧曉倩",25), new Person(4,"李小靜",67), new Person(4,"李小靜",18), new Person(4,"李小靜",67), new Person(5,"羅澤成",12) ); public static void main(String[] args){ tesst02(); }/** * collect(Collector c)方法 * 將流轉換爲其餘形式。接收一個 Collector接口的實現,用於給Stream中元素作彙總的方法 * Collector 接口中方法的實現決定了如何對流執行收集操做(如收集到 List、Set、Map)。 * 可是Collectors 實用類提供了不少靜態方法,能夠方便地建立常見收集器實例 */ public static void tesst02(){ //收集姓名放入list List<String> list2=list.stream() .map(Person::getName) .collect(Collectors.toList()); list2.forEach(System.out::println); System.out.println("--------------------------------------------"); //收集姓名放入set Set<String> set=list.stream() .map(Person::getName) .collect(Collectors.toSet()); set.forEach(System.out::println); System.out.println("--------------------------------------------"); //收集姓名放入指定的集合 LinkedHashSet<String> linkedHashSet=list.stream() .map(Person::getName) .collect(Collectors.toCollection(LinkedHashSet::new)); linkedHashSet.forEach(System.out::println); System.out.println("--------------------------------------------"); //收集總個數 Long count=list.stream() .collect(Collectors.counting()); System.out.println(count); System.out.println("--------------------------------------------"); //收集平均值 Double integer=list.stream() .collect(Collectors.averagingDouble(Person::getAge)); System.out.println(integer); System.out.println("--------------------------------------------"); //收集總和 Integer sum=list.stream() .collect(Collectors.summingInt(Person::getAge)); System.out.println(sum); System.out.println("--------------------------------------------"); //收集最大值 Optional<Person> optional=list.stream() .collect(Collectors.maxBy((p1,p2)->Integer.compare(p1.getAge(),p2.getAge()))); System.out.println(optional.get()); System.out.println("--------------------------------------------"); //收集最小值 Optional<Person> optional2=list.stream() .collect(Collectors.maxBy((p1,p2)->-Integer.compare(p1.getAge(),p2.getAge()))); System.out.println(optional2.get()); System.out.println("--------------------------------------------"); //分組,名字相同的分爲一組 Map<String,List<Person>> map=list.stream() .collect(Collectors.groupingBy(Person::getName)); System.out.println(map); System.out.println("--------------------------------------------"); //分組,名字相同的分爲一組,名字相同再根據年齡分組 Map<String,Map<String,List<Person>>> map1=list.stream() .collect(Collectors.groupingBy(Person::getName,Collectors.groupingBy((p)->{ if(p.getAge()>20){ return "青年"; }else{ return "老年"; } }))); System.out.println(map1); System.out.println("--------------------------------------------"); //分區,只能分爲兩個區,true和false兩個區,根據年齡40分爲兩個區 Map<Boolean,List<Person>> map2=list.stream() .collect(Collectors.partitioningBy((e)->e.getAge()>40)); System.out.println(map2); System.out.println("--------------------------------------------"); //求總和。平均值。總數、最大值、最小值等等 DoubleSummaryStatistics dss= list.stream() .collect(Collectors.summarizingDouble(Person::getAge)); System.out.println(dss.getSum()); System.out.println(dss.getAverage()); System.out.println(dss.getCount()); System.out.println("--------------------------------------------"); //把姓名按照「,」分割 String str=list.stream() .map(Person::getName) .collect(Collectors.joining(",")); System.out.println(str); System.out.println("--------------------------------------------"); } }

 

七:並行流和串行流

並行流就是把一個內容分紅多個數據塊,並用不一樣的線程分別處理每一個數據塊的流。Java 8 中將並行進行了優化,咱們能夠很容易的對數據進行並行操做。Stream API 能夠聲明性地經過 parallel() 與sequential() 在並行流與順序流之間進行切換。java8中的並行流底層就是採用的java的Fork/Join 框架

package com.bjsxt.forkJoin;

import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ForkJoinPool;
import java.util.stream.LongStream;

/**
 * Created by Administrator on 2019/3/7.
 */
public class TestForkJoin {

    public static void main(String[] args){
        test02();
    }

    

    public static void test02(){
        long start = System.currentTimeMillis();
        Long sum = LongStream.rangeClosed(0L, 10000000000L)
                .parallel()  //切換到並行流
                .sum();
        System.out.println(sum);
        long end = System.currentTimeMillis();
        System.out.println("耗費的時間爲: " + (end - start));
    }
}

這裏有必要簡單介紹下Fork/Join框架

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

 

 

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

 

案例:計算start與end之間的值的總和

package com.bjsxt.forkJoin;

import java.util.concurrent.RecursiveTask;

/**
 * 計算start與end之間的值的總和
 */
public class ForkJoinCalculate extends RecursiveTask<Long> {

    private long start;

    private long end;

    private static final long THRESHOLD = 100000L; //臨界值

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

    /**
     *
     * @return
     */
    @Override
    protected Long compute() {
        long length=end-start;
        if(length<THRESHOLD){   //已經小於臨界值
            long sum=0;
            for(long i=start;i<=end;i++){
                sum+=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.bjsxt.forkJoin;

import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ForkJoinPool;
import java.util.stream.LongStream;

/**
 * Created by Administrator on 2019/3/7.
 */
public class TestForkJoin {

    public static void main(String[] args){
        test01();
    }

    public static void test01(){
        Instant strat=Instant.now();
        ForkJoinPool forkJoinPool=new ForkJoinPool();
        ForkJoinCalculate forkJoinCalculate=new ForkJoinCalculate(0,100000000000L);
        long sum=forkJoinPool.invoke(forkJoinCalculate);
        System.out.println(sum);
        Instant end=Instant.now();
        System.out.println("計算耗時:"+ Duration.between(strat,end).toMillis());
    }

}

8、Optional 類

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

package com.bjsxt.optiona;

import com.bjsxt.Person;

import java.util.Optional;

/**
 * 1、Optional 容器類:用於儘可能避免空指針異常
 *     Optional.of(T t) : 建立一個 Optional 實例
 *     Optional.empty() : 建立一個空的 Optional 實例
 *     Optional.ofNullable(T t):若 t 不爲 null,建立 Optional 實例,不然建立空實例
 *     isPresent() : 判斷是否包含值
 *     orElse(T t) :  若是調用對象包含值,返回該值,不然返回t
 *     orElseGet(Supplier s) :若是調用對象包含值,返回該值,不然返回 s 獲取的值
 *     map(Function f): 若是有值對其處理,並返回處理後的Optional,不然返回 Optional.empty()
 *     flatMap(Function mapper):與 map 相似,要求返回值必須是Optional
 */
public class TestOptiona {

    public static void main(String[] args){
        test01();
    }

    public static void test01(){
        Optional<Person> optional=Optional.of(new Person());    //若是of()方法裏面的值爲null,下面一句會發生空指針異常
        Person p=optional.get();    //獲取Optional容器中的值
        System.out.println(p);
    }

    public static void test02(){
        Optional<Person> optional=Optional.empty(); //建立一個空的 Optional 實例
        Optional<Person> optiona2=Optional.ofNullable(new Person());//若 參數 不爲 null,建立 Optional 實例,不然建立空實例
    }

    public static void test03(){
        Optional<Person> optiona2=Optional.ofNullable(new Person());//若 參數 不爲 null,建立 Optional 實例,不然建立空實例
        if(optiona2.isPresent()){   //判斷Optional容器中是否包含值
            System.out.println("有值。。。");
        }

        Person p1 = optiona2.orElse(new Person(1,"張三",23)); //若是容器值爲空,則值爲orElse()方法的參數
        System.out.println(p1);

        Person p2 = optiona2.orElseGet(() -> new Person()); //同orElse(),只不過參數是一個函數式接口
        System.out.println(p2);
    }

    public void test04(){
        Optional<Person> op = Optional.of(new Person(1,"張三",23));

        Optional<String> op2 = op.map(Person::getName);
        System.out.println(op2.get());

        Optional<String> op3 = op.flatMap((e) -> Optional.of(e.getName()));
        System.out.println(op3.get());
    }

}

 九、新的時間與日期API

java以前的時間對象是可變的對象,存在線程安全問題,java8引入了全新的時間類

LocalDate、LocalTime、LocalDateTime 類的實例是不可變的對象,分別表示使用 ISO-8601日曆系統的日期、時間、日期和時間。它們提供了簡單的日期或時間,並不包含當前的時間信息。也不包含與時區相關的信息。

Instant 時間戳,用於「時間戳」的運算。它是以Unix元年(傳統的設定爲UTC時區1970年1月1日午夜時分)開始所經歷的描述進行運算

Duration:用於計算兩個「時間」間隔

Period:用於計算兩個「日期」間隔

TemporalAdjuster : 時間校訂器。有時咱們可能須要獲取例如:將日期調整到「下個週日」等操做。

TemporalAdjusters : 該類經過靜態方法提供了大量的經常使用 TemporalAdjuster 的實現。

java.time.format.DateTimeFormatter 類:該類提供了三種格式化方法:  預約義的標準格式  語言環境相關的格式  自定義的格式

相關文章
相關標籤/搜索