Stream
1.類的方法列表
創建該類Stream
是為了便於構建數據流鏈。為此,該類Stream<T>
具有返回新Stream
對象的方法。
這些數據流中的每一個都執行一個簡單的操作,但是如果您將它們組合成鏈並添加有趣的lambda 函數,那麼您將擁有一種強大的機制來生成您想要的輸出。很快你就會親眼看到。
以下是該類的方法Stream
(僅是最基本的方法):
方法 | 描述 |
---|---|
|
從一組對象創建一個流 |
|
根據指定規則生成流 |
|
連接兩個流 |
|
過濾數據,只傳遞符合指定規則的數據 |
|
刪除重複項。不傳遞已經遇到的數據 |
|
對數據進行排序 |
|
對流中的每個元素執行操作 |
|
返回一個被截斷的流,使其不超過指定的限制 |
|
跳過前 n 個元素 |
|
將數據從一種類型轉換為另一種類型 |
|
將數據從一種類型轉換為另一種類型 |
|
檢查流中是否至少有一個元素匹配指定的規則 |
|
檢查流中的所有元素是否都匹配指定的規則 |
|
檢查流中是否沒有元素匹配指定的規則 |
|
返回找到的第一個與規則匹配的元素 |
|
返回流中與規則匹配的任何元素 |
|
搜索數據流中的最小元素 |
|
返回數據流中的最大元素 |
|
返回數據流中元素的數量 |
|
從流中讀取所有數據並將其作為集合返回 |
Stream
2.班級的中間和終端操作
如您所見,並非上表中的所有方法都返回一個Stream
. Stream
這與類的方法可以分為中間(也稱為非終結)方法和終結方法有關。
中間方法
中間方法返回一個實現接口的對象Stream
,它們可以鏈接在一起。
終端方法
終端方法返回 a 以外的值Stream
。
方法調用管道
因此,您可以構建一個由任意數量的中間方法和最後一個終端方法調用組成的流管道。這種方法可以讓您實現相當複雜的邏輯,同時提高代碼的可讀性。
數據流中的數據根本不會改變。中間方法鍊是一種靈活的(聲明性的)方式,用於指定將在調用終端方法後執行的數據處理管道。
也就是說,如果終端方法沒有被調用,那麼數據流中的數據就不會被任何方式處理。只有在終端方法被調用後,數據才開始根據流管道中指定的規則進行處理。
stream()
.intemediateOperation1()
.intemediateOperation2()
...
.intemediateOperationN()
.terminalOperation();
中間方法和終端方法的比較:
中間的 | 終端 | |
---|---|---|
返回類型 | Stream |
不是Stream |
可以與同類型的多個方法組合形成管道 | 是的 | 不 |
單個管道中的方法數 | 任何 | 不超過一個 |
產生最終結果 | 不 | 是的 |
開始處理流中的數據 | 不 | 是的 |
讓我們看一個例子。
假設我們有一個動物愛好者俱樂部。明天俱樂部將慶祝姜貓日。俱樂部有寵物主人,每個人都有一份寵物清單。它們不僅限於貓。
任務:你需要找出所有姜貓的名字,為明天的“職業假期”製作個性化的賀卡。賀卡應按貓的年齡從大到小排序。
首先,我們提供一些類來幫助解決這個任務:
public enum Color {
WHITE,
BLACK,
DARK_GREY,
LIGHT_GREY,
FOXY,
GREEN,
YELLOW,
BLUE,
MAGENTA
}
public abstract class Animal {
private String name;
private Color color;
private int age;
public Animal(String name, Color color, int age) {
this.name = name;
this.color = color;
this.age = age;
}
public String getName() {
return name;
}
public Color getColor() {
return color;
}
public int getAge() {
return age;
}
}
public class Cat extends Animal {
public Cat(String name, Color color, int age) {
super(name, color, age);
}
}
public class Dog extends Animal {
public Dog(String name, Color color, int age) {
super(name, color, age);
}
}
public class Parrot extends Animal {
public Parrot(String name, Color color, int age) {
super(name, color, age);
}
}
public class Pig extends Animal {
public Pig(String name, Color color, int age) {
super(name, color, age);
}
}
public class Snake extends Animal {
public Snake(String name, Color color, int age) {
super(name, color, age);
}
}
public class Owner {
private String name;
private List<Animal> pets = new ArrayList<>();
public Owner(String name) {
this.name = name;
}
public List<Animal> getPets() {
return pets;
}
}
現在讓我們看一下Selector
類,其中將根據指定的標准進行選擇:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Selector {
private static List<Owner> owners;
private static void initData() {
final Owner owner1 = new Owner("Ronan Turner");
owner1.getPets().addAll(List.of(
new Cat("Baron", Color.BLACK, 3),
new Cat("Sultan", Color.DARK_GREY, 4),
new Dog("Elsa", Color.WHITE, 0)
));
final Owner owner2 = new Owner("Scarlet Murray");
owner2.getPets().addAll(List.of(
new Cat("Ginger", Color.FOXY, 7),
new Cat("Oscar", Color.FOXY, 5),
new Parrot("Admiral", Color.BLUE, 3)
));
final Owner owner3 = new Owner("Felicity Mason");
owner3.getPets().addAll(List.of(
new Dog("Arnold", Color.FOXY, 3),
new Pig("Vacuum Cleaner", Color.LIGHT_GREY, 8)
));
final Owner owner4 = new Owner("Mitchell Stone");
owner4.getPets().addAll(List.of(
new Snake("Mr. Boa", Color.DARK_GREY, 2)
));
final Owner owner5 = new Owner("Jonathan Snyder");
owner5.getPets().addAll(List.of(
new Cat("Fisher", Color.BLACK, 16),
new Cat("Zorro", Color.FOXY, 14),
new Cat("Margo", Color.WHITE, 3),
new Cat("Brawler", Color.DARK_GREY, 1)
));
owners = List.of(owner1, owner2, owner3, owner4, owner5);
}
}
剩下的就是向方法中添加代碼main
。目前,我們首先調用該initData()
方法,該方法填充俱樂部中的寵物主人列表。然後我們選擇按年齡降序排列的薑貓的名字。
首先,讓我們看一下不使用流來解決這個任務的代碼:
public static void main(String[] args) {
initData();
List<String> findNames = new ArrayList<>();
List<Cat> findCats = new ArrayList<>();
for (Owner owner : owners) {
for (Animal pet : owner.getPets()) {
if (Cat.class.equals(pet.getClass()) && Color.FOXY == pet.getColor()) {
findCats.add((Cat) pet);
}
}
}
Collections.sort(findCats, new Comparator<Cat>() {
public int compare(Cat o1, Cat o2) {
return o2.getAge() - o1.getAge();
}
});
for (Cat cat : findCats) {
findNames.add(cat.getName());
}
findNames.forEach(System.out::println);
}
現在讓我們看看另一種選擇:
public static void main(String[] args) {
initData();
final List<String> findNames = owners.stream()
.flatMap(owner -> owner.getPets().stream())
.filter(pet -> Cat.class.equals(pet.getClass()))
.filter(cat -> Color.FOXY == cat.getColor())
.sorted((o1, o2) -> o2.getAge() - o1.getAge())
.map(Animal::getName)
.collect(Collectors.toList());
findNames.forEach(System.out::println);
}
如您所見,代碼更加緊湊。此外,流管道的每一行都是一個動作,因此它們可以像英文句子一樣閱讀:
|
從一個移動Stream<Owner> 到一個Stream<Pet> |
|
只保留數據流中的貓 |
|
只保留數據流中的薑貓 |
|
按年齡降序排列 |
|
獲取名稱 |
|
將結果放入列表 |
3.創建流
該類Stream
具有三個我們尚未介紹的方法。這三個方法的目的是創建新的線程。
Stream<T>.of(T obj)
方法
該of()
方法創建一個由單個元素組成的流。當一個函數將一個Stream<T>
對像作為參數,但你只有一個T
對象時,通常需要這樣做。然後您可以輕鬆簡單地使用該of()
方法來獲取由單個元素組成的流。
例子:
Stream<Integer> stream = Stream.of(1);
Stream<T> Stream.of(T obj1, T obj2, T obj3, ...)
方法
該of()
方法創建一個由傳遞的元素組成的流。允許任意數量的元素。例子:
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
Stream<T> Stream.generate(Supplier<T> obj)
方法
該generate()
方法允許您設置一個規則,該規則將用於在請求時生成流的下一個元素。例如,您可以每次給出一個隨機數。
例子:
Stream<Double> s = Stream.generate(Math::random);
Stream<T> Stream.concat(Stream<T> a, Stream<T> b)
方法
該concat()
方法將兩個傳遞的流連接成一個。讀取數據時,首先從第一個流讀取,然後從第二個流讀取。例子:
Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> stream2 = Stream.of(10, 11, 12, 13, 14);
Stream<Integer> result = Stream.concat(stream1, stream2);
4.過濾數據
另外 6 種方法創建新的數據流,讓您將流組合成不同複雜度的鏈(或管道)。
Stream<T> filter(Predicate<T>)
方法
此方法返回一個新的數據流,該數據流根據傳遞的規則過濾源數據流。必須在類型為 的對像上調用該方法。Stream<T>
您可以使用lambda 函數指定過濾規則,然後編譯器會將其轉換為Predicate<T>
對象。
例子:
鍊式流 | 解釋 |
---|---|
|
只保留小於三的數字 |
|
只保留大於零的數字 |
Stream<T> sorted(Comparator<T>)
方法
此方法返回一個新的數據流,它對源流中的數據進行排序。您傳入一個comparator,它設置用於比較數據流的兩個元素的規則。
Stream<T> distinct()
方法
此方法返回一個新的數據流,它只包含源數據流中的唯一元素。丟棄所有重複數據。例子:
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 2, 2, 2, 3, 4);
Stream<Integer> stream2 = stream.distinct(); // 1, 2, 3, 4, 5
Stream<T> peek(Consumer<T>)
方法
此方法返回一個新的數據流,儘管其中的數據與源流中的數據相同。但是當從流中請求下一個元素時,您傳遞給該方法的函數peek()
將被調用。
如果將函數傳遞System.out::println
給peek()
方法,則所有對像在通過流時都會顯示出來。
Stream<T> limit(int n)
方法
此方法返回一個新的數據流,它只包含源數據流中的第一個n
元素。丟棄所有其他數據。例子:
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 2, 2, 2, 3, 4);
Stream<Integer> stream2 = stream.limit(3); // 1, 2, 3
Stream<T> skip(int n)
方法
此方法返回一個新的數據流,其中包含與源流相同的所有元素,但跳過(忽略)第一個n
元素。例子:
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 2, 2, 2, 3, 4);
Stream<Integer> stream2 = stream.skip(3); // 4, 5, 2, 2, 2, 3, 4
GO TO FULL VERSION