JAVA8的新特性 Stream流

一:概念

1,Stream是元素的集合,這點讓Stream看起來用些類似Iterator;
2,可以支持順序和並行的對原Stream進行匯聚的操作;

Stream就相當於一個高級版本的Iterator。原始版本的Iterator,用戶只能一個一個的遍歷元素並對其執行某些操作;高級版本的Stream,用戶只要給出需要對其包含的元素執行什麼操作,比如“過濾掉長度大於10的字符串”、“獲取每個字符串的首字母”等,具體這些操作如何應用到每個元素上,就給Stream就好了

二:代碼對比:
創建集合:

  1. 首先篩選所有姓張的人;
  2. 然後篩選名字有三個字的人;
  3. 最後進行對結果進行打印輸出。
public class NormalFilter {
  	public static void main(String[] args) {
      	List<String> list = new ArrayList<>();
        list.add("張無忌");
        list.add("周芷若");
        list.add("趙敏");
        list.add("張強");
        list.add("張三丰");

        List<String> zhangList = new ArrayList<>();
        for (String name : list) {
            if (name.startsWith("張")) {
              	zhangList.add(name);
            }
        }

        List<String> shortList = new ArrayList<>();
        for (String name : zhangList) {
            if (name.length() == 3) {
              	shortList.add(name);
            }
        }

        for (String name : shortList) {
          	System.out.println(name);
        }
    }
}

Stream流實現優雅的代碼書寫:

public class StreamFilter {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("張無忌");
        list.add("周芷若");
        list.add("趙敏");
        list.add("張強");
        list.add("張三丰");

        list.stream()
          	.filter(s -> s.startsWith("張"))
            .filter(s -> s.length() == 3)
            .forEach(s->System.out.println(s));
    }
}

三:流式思想概述
在這裏插入圖片描述

這張圖展示了Stream流的過濾,映射,跳過,計數等多步操作,這是一種集合元素的處理方案,而方案就是一種“函數模型”。圖中的每一個方框都是一個“流”,調用指定的方法,可以從一個流模型轉換爲另一個流模型。而最右側的數字3是最終結果。
這裏的filter、map、skip都是在對函數模型進行操作,集合元素並沒有真正被處理。只有當終結方法count執行的時候,整個模型纔會按照指定策略執行操作。而這得益於Lambda的延遲執行特性。
備註:“Stream流”其實是一個集合元素的函數模型,它並不是集合,也不是數據結構,其本身並不存儲任何元素(或其地址值)。

四:如何獲取流:
java.util.stream.Stream是Java 8新加入的最常用的流接口。(這並不是一個函數式接口。)

獲取一個流非常簡單,有以下幾種常用的方式:

  • 所有的Collection集合都可以通過stream默認方法獲取流;
  • Stream接口的靜態方法of可以獲取數組對應的流。

分爲三種情況:單列集合(list,set),雙列集合(Map),數組
方式1 : 根據Collection獲取流
首先,java.util.Collection接口中加入了default方法stream用來獲取流,所以其所有實現類均可獲取流。

import java.util.*;
import java.util.stream.Stream	;

public class GetStream {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        // ...
        Stream<String> stream1 = list.stream();

        Set<String> set = new HashSet<>();
        // ...
        Stream<String> stream2 = set.stream();

        Vector<String> vector = new Vector<>();
        // ...
        Stream<String> stream3 = vector.stream();
    }
}

方式2 : 根據Map獲取流

java.util.Map接口不是Collection的子接口,且其K-V數據結構不符合流元素的單一特徵,所以獲取對應的流需要分key、value或entry等情況:

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;

public class MapGetStream {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        // ...
        Stream<String> keyStream = map.keySet().stream();
        Stream<String> valueStream = map.values().stream();
        Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
    }
}

方式3 : 根據數組獲取流

如果使用的不是集合或映射而是數組,由於數組對象不可能添加默認方法,所以Stream接口中提供了靜態方法of,使用很簡單:

import java.util.stream.Stream;

public class ArrGetStream {
    public static void main(String[] args) {
        String[] array = { "張無忌", "張翠山", "張三丰", "張一元" };
        Stream<String> stream = Stream.of(array);
    }
}

備註:of方法的參數其實是一個可變參數,所以支持數組。

五:常用方法:
流模型的操作很豐富,這裏介紹一些常用的API。這些方法可以被分成兩種:

  • 終結方法:返回值類型不再是Stream接口自身類型的方法,因此不再支持類似StringBuilder那樣的鏈式調用。本小節中,終結方法包括count和forEach方法。
  • 非終結方法:返回值類型仍然是Stream接口自身類型的方法,因此支持鏈式調用。(除了終結方法外,其餘方法均爲非終結方法。)

備註:本小節之外的更多方法,請自行參考API文檔。

forEach : 逐一處理

雖然方法名字叫forEach,但是與for循環中的“for-each”暱稱不同,該方法並不保證元素的逐一消費動作在流中是被有序執行的。

void forEach(Consumer<? super T> action);

該方法接收一個Consumer接口函數,會將每一個流元素交給該函數進行處理。例如:

import java.util.stream.Stream;

public class Demo12StreamForEach {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("張無忌", "張三丰", "周芷若");
        stream.forEach(s->System.out.println(s));
    }
}

在這裏,方法引用System.out::println就是一個Consumer函數式接口的示例。

count:統計個數

正如舊集合Collection當中的size方法一樣,流提供count方法來數一數其中的元素個數:

long count();

該方法返回一個long值代表元素個數(不再像舊集合那樣是int值)。基本使用:

public class StreamCount {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("張無忌", "張三丰", "周芷若");
        Stream<String> result = original.filter(s -> s.startsWith("張"));
        System.out.println(result.count()); // 2
    }
}

filter:過濾

可以通過filter方法將一個流轉換成另一個子集流。方法聲明:

Stream<T> filter(Predicate<? super T> predicate);

該接口接收一個Predicate函數式接口參數(可以是一個Lambda或方法引用)作爲篩選條件。

基本使用

Stream流中的filter方法基本使用的代碼如:

public class Demo07StreamFilter {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("張無忌", "張三丰", "周芷若");
        Stream<String> result = original.filter(s -> s.startsWith("張"));
    }
}

在這裏通過Lambda表達式來指定了篩選的條件:必須姓張。

limit:取用前幾個

limit方法可以對流進行截取,只取用前n個。方法簽名:

Stream<T> limit(long maxSize);

參數是一個long型,如果集合當前長度大於參數則進行截取;否則不進行操作。基本使用:

import java.util.stream.Stream;

public class Demo10StreamLimit {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("張無忌", "張三丰", "周芷若");
        Stream<String> result = original.limit(2);
        System.out.println(result.count()); // 2
    }
}

skip:跳過前幾個

如果希望跳過前幾個元素,可以使用skip方法獲取一個截取之後的新流:

Stream<T> skip(long n);

如果流的當前長度大於n,則跳過前n個;否則將會得到一個長度爲0的空流。基本使用:

import java.util.stream.Stream;

public class StreamSkip {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("張無忌", "張三丰", "周芷若");
        Stream<String> result = original.skip(2);
        System.out.println(result.count()); // 1
    }
}

map:映射

如果需要將流中的元素映射到另一個流中,可以使用map方法。方法簽名:

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

該接口需要一個Function函數式接口參數,可以將當前流中的T類型數據轉換爲另一種R類型的流。

基本使用

Stream流中的map方法基本使用的代碼如:

 import java.util.stream.Stream;
    
    public class DemoStreamMap {
        public static void main(String[] args) {
            Stream<String> original = Stream.of("10", "12", "18");
            Stream<Integer> result = original.map(Integer::parseInt);
        }
    }

這段代碼中,map方法的參數通過方法引用,將字符串類型轉換成爲了int類型(並自動裝箱爲Integer類對象)。

concat:組合

如果有兩個流,希望合併成爲一個流,那麼可以使用Stream接口的靜態方法concat:

static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)

備註:這是一個靜態方法,與java.lang.String當中的concat方法是不同的。

該方法的基本使用代碼如:

import java.util.stream.Stream;

public class DemoStreamConcat {
    public static void main(String[] args) {
        Stream<String> streamA = Stream.of("張無忌");
        Stream<String> streamB = Stream.of("張翠山");
        Stream<String> result = Stream.concat(streamA, streamB);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章