Java8新特性--流(Stream)

一、簡介
html

     Java 8是Java自Java 5(發佈於2004年)以後的最重要的版本。這個版本包含語言、編譯器、庫、工具和JVM等方面的十多個新特性。在本文中咱們一塊兒來學習引入的一個新特性-流java

 

二、Lambda表達式數組

    在學習流以前,咱們先來了解下java8中引入的Lambda表達式,它做爲流很重要的一部分。Lambda表達式個構成以下所示:oracle

    

2.1  有效的Lambda表達式app

()-> 42 (Apple a) -> { return a.getWeight() > 150; } //對於函數只有一行代碼的,能夠去掉大括號{}以及return關鍵字 (String s)-> s.length() (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())

 

2.2  方法引用
函數

     當你須要使用方法引用時,目標放在分隔符::前,方法的名稱放在後面。例如:Apple::getWeight就是引用了Apple類中定義的方法getWeight。請記住,不須要括號,由於你工具

沒有實際調用這個方法。方法引用就是Lambda表達式(Apple a) -> a.getWeight的快捷寫法。學習

      Lambda及其等效方法引用的列子:spa

    

     方法引用主要有三類:.net

  •    指向靜態方法的方法引用(例如Integer的parseInt方法,寫做Integer::parseInt)
  •    指向任意類型實例方法的方法引用(例如String的length方法,寫做String::length)
  •    指向現有對象的實例方法的方法引用(假設你有一個局部變量expensiveTransaction用於存放Transaction類型的對象,它支持實例方法getValue,那麼你就能夠寫成expensiveTransaction::getValue)

       第二種和第三種方法引用可能乍看起來有點兒暈。相似於String::length的第二種方法引用的思想就是你在引用一個對象的方法,而這個對象自己是Lambda的一個參數。例如,Lambda表達

       式(String s) -> s.toUppeCase()能夠寫做String::toUpperCase。但第三種方法引用指的是,你在Lambda中調用一個已經存在的外部對象中的方法。例如,Lambda表達式()->expensiveTransaction.getValue()可

       以寫做expensiveTransaction::getValue。

 

2.3  構造函數引用

Supplier<Apple> c1 = Apple::new;     // 構造函數引用指向默認的Apple()構造函數 Apple apple = c1.get(); // 調用Supplier的get方法將產生一個新的Apple  Function<Integer, Apple> c2 = Apple::new; // 指向Apple(Integer weight)的構造函數引用 Apple apple2 = c2.apply(110); // 調用該Function函數的apply方法,並給出要求的重量,將產生一個Apple

 

     在下面的代碼中,一個Integer構成的List中的每一個元素都經過咱們前面定義的類的map方法傳遞給了Apple的構造函數,獲得了一個具備不一樣重量蘋果的List:

private static void test() { List<Integer> weights = Arrays.asList(7, 3, 4, 10); List<Apple> apples = map(weights, Apple::new); apples.stream().map(Apple::getWeight).forEach(System.out::println); } public static List<Apple> map(List<Integer> list, Function<Integer, Apple> f) { List<Apple> result = new ArrayList<>(); for (Integer e : list) { result.add(f.apply(e)); }   return result; }

 

  三、引入流

     3.1流簡介

          流是一系列數據項,一次只生成一項。程序能夠從輸入流中一個一個讀取數據項,而後以一樣的方式將數據項寫入輸出流。一個程序的輸出流極可能是另外一個程序的輸入流。就想汽車組裝流水線一

    樣,汽車排隊進入加工站,每一個加工站會接收、修改汽車,而後將之傳遞給下一站作進一步的處理。儘管流水線其實是一個序列,但不一樣加工站的運行通常是並行的。

          基於此,Java8能夠透明的把輸入的不想管部分拿到幾個CPU內核上去分別執行你的Stream操做流水線——這是幾乎免費的並行,用不着費勁搞Thread了。

      3.2流操做

            流操做包含3個部分:數據源、中間操做、終端操做

               數據源:集合、數組

      中間操做:能夠鏈接起來的流操做,流程一條流水線

      終端操做:關閉流的操做

    

    中間操做和終端操做,以下圖所示:

    

 

   3.3 使用流

        篩選和切片:filter()、distinct()、limit()、skip()

               映射:map()

     查找和匹配:anyMatch()、allMatch()、noneMatch()、findAny()、findFirst()

     歸約:reduce()

       

   3.3.1 篩選和切片

              filter():篩選過濾。會接受一個謂詞(一個返回boolean的函數)做爲參數,並返回一個包括全部符合謂詞的元素的流。

    distinct():去重。它會返回一個元素各異(根據流所生成元素的hashCode和equals方法實現)的流

    limit(n):截斷。它會返回一個不超過給定長度的流,若是流是有序的,則最多返回前n個元素

    skip(n):跳過。返回一個扔掉了前n個元素的流

List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4,5, 6); numbers.stream() .filter(i -> i % 2 == 0) //篩選出能被2整除的 .distinct() //去重 .limit(3) //取前3個元素 .skip(1) //扔掉第一個元素 .forEach(System.out::println);

 

  3.3.2 映射

    map():映射。它會接受一個函數做爲參數。這個函數會被應用到每一個元素上,並將其映射成一個新的元素

    例如,下面的代碼,提取菜單中菜的名字

List<String> nameList = menu.stream()
  .map(Dish::getName)
  .collect(Collectors.toList())

  3.3.4 查找和匹配

    anyMatch():流中是否有一個元素能匹配給定的謂詞

List<Integer> numbers = Arrays.asList(6, 5, 4, 8, 1, 2, 3, 4, 2, 5, 6, 8, 10); numbers.stream().anyMatch(d -> d % 3 == 0) ; //true

 

    allMatch():流中的元素是否都能匹配給定的謂詞

 

numbers.stream().allMatch(d -> d % 3 == 0); //false

 

    noneMatch():與allMatch()相對,流中沒有任何元素與給定的謂詞匹配

 

numbers.stream().noneMatch(d -> d % 7 == 0); //true

 

    findAny():返回當前流中的任意元素。不過該方法返回的是Optional<T>容器類,其方法包括

          一、isPresent() 將在Optional包含值的時候返回true,否者返回false

          二、T get() 會在值存在時返回值,不然拋出一個NoSuchElement異常

Optional<Integer> nums = numbers.stream().filter(d -> d % 4 == 0).findAny(); nums.isPresent(); //true nums.get(); //4

 

    findFirst():找到第一個元素,工做方式相似於findAny()

 

  3.3.5 歸約

    把一個流中的元素組合起來,使用reduce操做來表達更復雜的查詢,好比「計算菜單中的總卡路里」或「菜單中卡路里最高的菜是哪個」。此類查詢須要將流中全部元素反覆結合起來,得

  到一個值,好比一個Integer。這樣的查詢能夠被歸類爲歸約操做(將流歸約成一個值)

    元素累加、累乘

List<Integer> numbers = Arrays.asList(4, 5, 3, 9); //有初始值: numbers.stream().reduce(0, (a, b) -> a + b); //21 numbers.stream().reduce(0, Integer::sum); //21 numbers.stream().reduce(1, (a, b) -> a * b); //540 //無初始值: Optional<Integer> sum = numbers.stream().reduce((a, b) -> (a + b)); //對於無初始值的歸約,reduce操做沒法返回其和,只能將結果包裹在一個Optional對象裏,以代表可能不存在 sum.get(); //21

    最大值、最小值

List<Integer> numbers = Arrays.asList(4, 5, 3, 9); Optional<Integer> max = numbers.stream().reduce(Integer::max); //最大值 max.get(); //9  Optional<Integer> min = numbers.stream().reduce(Integer::min); //最小值 min.get(); //3

  求和、平均數

int total = menu.stream().collect(Collectors.summingInt(Dish::getCalories));     //統計總數 double avg = menu.stream().collect(Collectors.averagingInt(Dish::getCalories)); // 求平均數 long count = menu.stream().count(); //個數  IntSummaryStatistics summ = menu.stream().collect(Collectors.summarizingInt(Dish::getCalories)); summ.getAverage(); //平均數 summ.getCount(); //個數 summ.getMax(); //最大值 summ.getMin(); //最小值 summ.getSum(); //

 

 3.4 用流收集數據

  收集器(collect):是一個將數據流縮減爲一個值的高級歸約操做,這個值能夠是集合、映射、或者一個值對象。你可使用collect達到如下目的:

    一、將數據流縮減爲一個單一值

    二、將一個數據流中的元素進行分組

    三、分割一個流中的元素

 

public List<String> getList(List<Task> tasks) {        //將數據收集進一個列表 return tasks.stream().map(Task::getTitle).collect(Collectors.toList()); } public Set<String> getSet(List<Task> tasks) { //將數據收集進一個集合 return tasks.stream().map(Task::getTitle).collect(Collectors.toSet()); } private static Map<String, Task> taskMap(List<Task> tasks) { //將數據收集進一個映射 return tasks.stream().collect(Collectors.toMap(Task::getTitle, task -> task)); } private static Map<TaskType, List<Task>> groupTasksByType(List<Task> tasks) { //分組 return tasks.stream().collect(Collectors.groupingBy(Task::getType)); } 

 

其它參照資源:http://blog.csdn.net/u013291394/article/details/52662761

相關文章
相關標籤/搜索