說到Stream便容易想到I/O Stream,而實際上,誰規定“流”就一定是“IO流”呢?在Java 8中,得益于Lambda所帶 來的函式式編程,引入了一個全新的Stream概念,用于解決已有集合類別庫既有的弊端,
流式思想概述
流式思想類似于工廠車間的“生產流水線”,當需要對多個元素進行操作(特別是多步操作)的時候,考慮到性能及便利性,我們應該首先拼好一個“模型”步驟方案,然后再按照方案去執行它,
“Stream流”其實是一個集合元素的函式模型,它并不是集合,也不是資料結構,其本身并不存盤任何 元素(或其地址值),
Stream(流)是一個來自資料源的元素佇列 :
元素是特定型別的物件,形成一個佇列, Java中的Stream并不會存盤元素,而是按需計算,
資料源 流的來源, 可以是集合,陣列 等,
和以前的Collection操作不同, Stream操作還有兩個基礎的特征:
Pipelining: 中間操作都會回傳流物件本身, 這樣多個操作可以串聯成一個管道, 如同流式風格(fluentstyle),這樣做可以對操作進行優化, 比如延遲執行(laziness)和短路( short-circuiting),
內部迭代:以前對集合遍歷都是通過Iterator或者增強for的方式, 顯式的在集合外部進行迭代, 這叫做外部迭代,Stream提供了內部迭代的方式,流可以直接呼叫遍歷方法,
當使用一個流的時候,通常包括三個基本步驟:獲取一個資料源(source)→ 資料轉換→執行操作獲取想要的結 果,每次轉換原有 Stream 物件不改變,回傳一個新的 Stream 物件(可以有多次轉換),這就允許對其操作可以 像鏈條一樣排列,變成一個管道,
獲取流
java.util.stream.Stream 是Java 8新加入的最常用的流介面,(這并不是一個函式式介面,)
獲取一個流非常簡單,有以下幾種常用的方式:
所有的 Collection 集合都可以通過 stream 默認方法獲取流;
Stream 介面的靜態方法 of 可以獲取陣列對應的流,
1、根據Collection獲取流
首先, java.util.Collection 介面中加入了default方法 stream 用來獲取流,所以其所有實作類均可獲取流,
import java.util.*;
import java.util.stream.Stream;
public class Demo1{
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 Demo2 {
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 Demo3 {
public static void main(String[] args) {
String[] array = { "曹操", "劉備", "孫權", "諸葛亮","司馬懿" };
Stream<String> stream = Stream.of(array);
}
}
常用方法
流模型的操作很豐富,這里介紹一些常用的API,這些方法可以被分成兩種:
延遲方法:
回傳值型別仍然是 Stream 介面自身型別的方法,因此支持鏈式呼叫,(除了終結方法外,其余方法均為延遲方法,)
終結方法:
回傳值型別不再是 Stream 介面自身型別的方法,因此不再支持類似 StringBuilder 那樣的鏈式呼叫,本小節中,終結方法包括 count 和forEach 方法,
逐一處理:forEach
雖然方法名字叫 forEach ,但是與for回圈中的“for-each”昵稱不同,
void forEach(Consumer<? super T> action);
該方法接收一個 Consumer 介面函式,會將每一個流元素交給該函式進行處理,
基本使用:
import java.util.stream.Stream;
public class Demo4 {
public static void main(String[] args) {
Stream<String> stream = Stream.of("曹操", "曹丕", "曹植");
stream.forEach(name-> System.out.println(name));
}
}
過濾:filter
可以通過 filter 方法將一個流轉換成另一個子集流,方法簽名:
Stream<T> filter(Predicate<? super T> predicate);
該介面接收一個 Predicate 函式式介面引數(可以是一個Lambda或方法參考)作為篩選條件,
基本使用:
import java.util.stream.Stream;
public class Demo5 {
public static void main(String[] args) {
Stream<String> original = Stream.of("劉備", "關羽", "張飛");
Stream<String> result = original.filter(s -> s.startsWith("張"));
}
}
在這里通過Lambda運算式來指定了篩選的條件:必須姓張,
映射:map
如果需要將流中的元素映射到另一個流中,可以使用 map 方法,方法簽名:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
該介面需要一個 Function 函式式介面引數,可以將當前流中的T型別資料轉換為另一種R型別的流,
基本使用:
import java.util.stream.Stream;
public class Demo6{
public static void main(String[] args) {
Stream<String> original = Stream.of("15", "12", "18");
Stream<Integer> result = original.map(str->Integer.parseInt(str));
}
}
統計個數:count
正如舊集合 Collection 當中的 size 方法一樣,流提供 count 方法來數一數其中的元素個數:
long count();
import java.util.stream.Stream;
public class Demo7 {
public static void main(String[] args) {
Stream<String> original = Stream.of("孫堅", "孫策", "孫權","孫亮","孫休","孫皓","周瑜","魯肅","呂蒙","陸遜","陸抗");
Stream<String> result = original.filter(s -> s.startsWith("孫"));
System.out.println(result.count());
}
}
取用前幾個:limit
limit 方法可以對流進行截取,只取用前n個,方法簽名:
Stream<T> limit(long maxSize);
引數是一個long型,如果集合當前長度大于引數則進行截取;否則不進行操作,基本使用:
import java.util.stream.Stream;
public class Demo8 {
public static void main(String[] args) {
Stream<String> original = Stream.of("張遼", "張郃", "樂進");
Stream<String> result = original.limit(2);
System.out.println(result.count());
}
}
跳過前幾個:skip
如果希望跳過前幾個元素,可以使用 skip 方法獲取一個截取之后的新流:
Stream<T> skip(long n);
如果流的當前長度大于n,則跳過前n個;否則將會得到一個長度為0的空流,基本使用:
import java.util.stream.Stream;
public class Demo9 {
public static void main(String[] args) {
Stream<String> original = Stream.of("李典", "許褚", "典韋","于禁");
Stream<String> result = original.skip(2);
System.out.println(result.count());
}
}
組合:concat
如果有兩個流,希望合并成為一個流,那么可以使用 Stream 介面的靜態方法 concat :
static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
該方法的基本使用代碼如:
import java.util.stream.Stream;
public class Demo10 {
public static void main(String[] args) {
Stream<String> streamA = Stream.of("夏侯惇");
Stream<String> streamB = Stream.of("夏侯淵");
Stream<String> result = Stream.concat(streamA, streamB);
}
}
案例:
現在有兩個 ArrayList 集合存盤隊伍當中的多個成員姓名,要求使用Stream依次進行以 下若干操作步驟:
- 第一個隊伍只要名字為3個字的成員姓名;存盤到一個新集合中,
- 第一個隊伍篩選之后只要前3個人;存盤到一個新集合中,
- 第二個隊伍只要姓夏侯的成員姓名;存盤到一個新集合中,
- 第二個隊伍篩選之后不要前2個人;存盤到一個新集合中,
- 將兩個隊伍合并為一個隊伍;存盤到一個新集合中,
- 根據姓名創建 Person 物件;存盤到一個新集合中,
- 列印整個隊伍的Person物件資訊,
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class Demo11 {
public static void main(String[] args) {
List<String> one = new ArrayList<>();
one.add("曹操");
one.add("曹昂");
one.add("曹丕");
one.add("夏侯恩");
one.add("夏侯楙");
one.add("夏侯玄");
List<String> two = new ArrayList<>();
two.add("夏侯惇");
two.add("夏侯淵");
two.add("夏侯霸");
two.add("夏侯尚");
two.add("曹植");
two.add("曹彰");
two.add("曹熊");
two.add("曹睿");
Stream<String> streamOne = one.stream().filter(s -> s.length() == 2).limit(3);
Stream<String> streamTwo = two.stream().filter(s -> s.startsWith("夏侯")).skip(2);
Stream.concat(streamOne, streamTwo).map(Person::new).forEach(System.out::println);
}
}
class Person{
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/294091.html
標籤:java
