Java8 中的Stream API詳解

Java 8 中Stream API詳解

Stream的作用

  • Java8 中的Stream是對集合對象功能的增強,專注於對集合對象進行各種非常便利、高效的聚合操作,或者大批量數據操作
  • Stream API藉助於同樣新出的Lambda表達式,極大的提高了編程效率和程序的可讀性
  • 同時提供串行和並行兩種模式進行匯聚操作,併發模式可以充分利用多核處理器的優勢,使用fork/join並行方式來拆分任務和加速處理過程。
  • 無需編寫一行多線程代碼就可以方便的寫出高性能的併發程序

Stream的性質

  • 流(Stream):就是數據渠道,用於操作數據源(集合、數組)所生成的元素序列
  • 集合注重的數據,流注重與計算
  • Stream不會自己存儲數據
  • Stream不會改變源對象。相反,他們會返回一個持有結果的新Stream
  • Stream操作是延遲執行的。這意味着會等到需要結果的時候才執行。

什麼是聚合操作

​ 在傳統J2EE的應用中,java代碼經常不得不依賴於關係型數據庫的聚合操作來完成諸如:

  • 每日平均消費金額
  • 最受歡迎的商品
  • 取一定量的數據樣本作爲首頁推薦

這類的操作。而在java集合API中,僅僅有極少量的輔助型方法,更多的時候需要用Iterator來遍歷集合,完成相關的聚合應用邏輯。

舉例:

獲取數組最大值

 public static void main(String[] args) { 
        int[] nums={1,2,3,4,5,6,7};
        int max = Arrays.stream(nums).max().getAsInt();
        System.out.println(max);
    }

Stream總覽

什麼是流

​ Stream不是集合元素,不是數據結構並不保存數據,是有關算法和計算的,類似於一個高版本的Iterator。我們只需要給出需要對其包含的元素執行什麼操作,Stream就會隱式地在內部進行遍歷,做出相應的數據轉換。

​ Stream如同一個迭代器,單向,不可往復,數據只能遍歷一次,遍歷一次後即用盡了。

​ Stream可以並行化操作,迭代器只能命令式地、串行化操作。

​ Stream的一大特點就是數據源本身可以是無限的。

流的構成

分爲三個基本步驟

​ 獲取一個數據源——數據轉化——執行操作獲取先要的結構

每次轉換原有的Stream對象不改變,返回一個新的Stream對象(可以有多次轉化),這就允許對其操作可以向鏈條一樣排列,變成一個管道

有多種方式生成Stream Source:

  • 從Collection和數組
    • Collection.stream()
    • Collection.oarallelStream()
    • Arrays.stream(T array)or Strean.of()
  • 從BufferReader
    • java.io.BUfferedReader.lines()
  • 靜態工程
  • 自己創建

流的操作類型

  • Intermediate:其主要目的是打開流,做出某種程度的數據映射/過濾,然後返回一個新的流,交給下一個操作使用。僅僅調用到這類方法,並沒有真正開始流的遍歷
  • Terminal:一個流只能有一個terminal操作,其操作的執行纔是真正開始流的遍歷,並生成一個結果

流的使用詳解

流的構造與轉換

  • 構造流的幾種常見方法
public static void main(String[] args) {
        // 1. Individual values
        Stream stream = Stream.of("a", "b", "c");
        // 2. Arrays
        String [] strArray = new String[] {"a", "B", "c"};
        stream = Stream.of(strArray);
        stream = Arrays.stream(strArray);
        // 3. Collections
        List<String> list = Arrays.asList(strArray);
        stream = list.stream();
    }
  • 數值流的構造
IntStream.of(new int[]{1, 2, 3}).forEach(System.out::println);
IntStream.range(1, 3).forEach(System.out::println);
IntStream.rangeClosed(1, 3).forEach(System.out::println);
  • 流轉換爲其他數據結構
// 1. Array
String[] strArray1 = stream.toArray(String[]::new);
// 2. Collection
List<String> list1 = stream.collect(Collectors.toList());
List<String> list2 = stream.collect(Collectors.toCollection(ArrayList::new));
Set set1 = stream.collect(Collectors.toSet());
Stack stack1 = stream.collect(Collectors.toCollection(Stack::new));
// 3. String
String str = stream.collect(Collectors.joining()).toString();

流的操作

  • 多箇中間操作可以連接起來形成一個流水線,除非流水線上觸發終止操作,否則中間操作不會執行任何處理,而在終止操作時一次性全部處理。即惰性求值。Stream的中間操作時不會有任何結果數據輸出的。
  • Stream的中間操作可以在整體上分爲:篩選與切片、映射、排序
篩選與切片
方法 描述
filter(Predicate p) 接收Lambda表達式,從流中排除某些元素
distinct() 篩選,通過流所生成元素的hashCode()和equals()去除重複元素
limit(long maxSize) 截斷流,使其元素不能超過給定數量
skip(long n) 跳過元素,返回一個扔掉了前n個元素的流。若流中的元素不足n個,則返回一個空流與limit(n)互補
示例
  • Employee類
public class Employee implements Serializable {
    private String name;
    private Integer age;
    private Double salary;
}
  • 構件對象數組
protected List<Employee> list = Arrays.asList(
    new Employee("張三", 18, 9999.99),
    new Employee("李四", 38, 5555.55),
    new Employee("王五", 60, 6666.66),
    new Employee("趙六", 8, 7777.77),
    new Employee("田七", 58, 3333.33)
);
  • Filter()方法

主要是接收Lambda表達式,從流中排除某些元素

//內部迭代:在此過程中沒有進行過迭代,由Stream api進行迭代
//中間操作:不會執行任何操作
Stream<Person> stream = list.stream().filter((e) -> {
    System.out.println("Stream API 中間操作");
    return e.getAge() > 30;
});
  • limit()方法

主要作用爲:截斷流,使其元素不超過給定數量

//過濾之後取2個值
list.stream().filter((e) -> e.getAge() >30 ).limit(2).forEach(System.out :: println);

​ 在其中我們可以配合其他的中間操作,並截斷流,使我們可以取得相應個數的元素。

  • skip()方法

跳過元素,返回一個扔掉了前n個元素的流。若流中元素不足n個,則返回一個空流。與limit(n)互補

//跳過前2個值
list.stream().skip(2).forEach(System.out :: println);
  • distinct()方法

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

list.stream().distinct().forEach(System.out :: println);

distict需要在實體中重寫hashCode和equal方法才能使用

映射
方法 描述
map(Function f) 接收一個函數,該函數會被引用到每個元素上,並將其映射成一個新的元素
mapToDouble(ToDoubleFunction f) 接收一個函數,該函數會被引用到每個元素上,產生一個新的DoubleStream
mapToInt(ToIntFunction f) 接收一個函數,該函數會被引用到每個元素上,產生一個新的IntStream
mapToLong(ToLongFunction f) 接收一個函數,該函數會被引用到每個元素上,產生一個新的LongStream
flagMap(function f) 接收一個函數,將流中的每個值都換成另一個流,然後把所有流,連接成一個流。
示例
  • map() 方法

    //將流中每一個元素都映射到map的函數中,每個元素執行這個函數,再返回
    List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
    list.stream().map((e) -> e.toUpperCase()).forEach(System.out::printf);
    
    //獲取Person中的每一個人得名字name,再返回一個集合
    List<String> names = this.list.stream().map(Person :: getName).collect(Collectors.toList());
    
  • flagMap()

    /**
         * flatMap —— 接收一個函數作爲參數,將流中的每個值都換成一個流,然後把所有流連接成一個流
         */
        @Test
        public void testFlatMap () {
            StreamAPI_Test s = new StreamAPI_Test();
            List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
            list.stream().flatMap((e) -> s.filterCharacter(e)).forEach(System.out::println);
    
            //如果使用map則需要這樣寫
            list.stream().map((e) -> s.filterCharacter(e)).forEach((e) -> {
                e.forEach(System.out::println);
            });
        }
    
        /**
         * 將一個字符串轉換爲流
         */
        public Stream<Character> filterCharacter(String str){
            List<Character> list = new ArrayList<>();
            for (Character ch : str.toCharArray()) {
                list.add(ch);
            }
            return list.stream();
        }
    

其實map方法相當於Collection的add方法,如果add的是個集合的話會變爲二維數組,而flatMap相當於Collection的addAll犯法

排序
方法 描述
sorted() 產生一個新流,其按自然順序排序
sort(Comparator comp) 產生一個新流,其中按比較器書序排序
// 自然排序
List<Employee> persons = list.stream().sorted().collect(Collectors.toList());

//定製排序
List<Employee> persons1 = list.stream().sorted((e1, e2) -> {
    if (e1.getAge() == e2.getAge()) {
        return 0;
    } else if (e1.getAge() > e2.getAge()) {
        return 1;
    } else {
        return -1;
    }
}).collect(Collectors.toList());
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章