1. รายการวิธีการของStream
ชั้นเรียน
คลาส นี้Stream
ถูกสร้างขึ้นเพื่อให้ง่ายต่อการสร้างห่วงโซ่ของสตรีมข้อมูล เพื่อให้บรรลุเป้าหมายนี้Stream<T>
คลาสมีเมธอดที่ส่งคืนStream
อ็อบเจกต์ ใหม่
สตรีมข้อมูลแต่ละรายการมีการดำเนินการง่ายๆ เพียงอย่างเดียว แต่ถ้าคุณรวมเข้าด้วยกันเป็นสายโซ่และเพิ่มฟังก์ชันแลมบ์ดา ที่น่าสนใจ คุณก็จะมีกลไกที่มีประสิทธิภาพในการสร้างเอาต์พุตที่คุณต้องการ ในไม่ช้าคุณจะเห็นด้วยตัวคุณเอง
นี่คือวิธีการของStream
ชั้นเรียน (เฉพาะวิธีพื้นฐานที่สุด):
วิธีการ | คำอธิบาย |
---|---|
|
สร้างกระแสจากชุดของวัตถุ |
|
สร้างสตรีมตามกฎที่ระบุ |
|
เชื่อมต่อสองกระแส |
|
กรองข้อมูล ส่งผ่านเฉพาะข้อมูลที่ตรงกับกฎที่ระบุเท่านั้น |
|
ลบรายการที่ซ้ำกัน ไม่ส่งต่อข้อมูลที่พบแล้ว |
|
จัดเรียงข้อมูล |
|
ดำเนินการกับแต่ละองค์ประกอบในสตรีม |
|
ส่งกลับสตรีมที่ถูกตัดให้มีความยาวไม่เกินขีดจำกัดที่ระบุ |
|
ข้ามองค์ประกอบ n ตัวแรก |
|
แปลงข้อมูลจากประเภทหนึ่งไปเป็นอีกประเภทหนึ่ง |
|
แปลงข้อมูลจากประเภทหนึ่งไปเป็นอีกประเภทหนึ่ง |
|
ตรวจสอบว่ามีองค์ประกอบอย่างน้อยหนึ่งรายการในสตรีมที่ตรงกับกฎที่ระบุหรือไม่ |
|
ตรวจสอบว่าองค์ประกอบทั้งหมดในสตรีมตรงกับกฎที่ระบุหรือไม่ |
|
ตรวจสอบว่าไม่มีองค์ประกอบใดในสตรีมที่ตรงกับกฎที่ระบุ |
|
ส่งกลับองค์ประกอบแรกที่พบว่าตรงกับกฎ |
|
ส่งกลับองค์ประกอบใด ๆ ในสตรีมที่ตรงกับกฎ |
|
ค้นหาองค์ประกอบขั้นต่ำในสตรีมข้อมูล |
|
ส่งกลับองค์ประกอบสูงสุดในสตรีมข้อมูล |
|
ส่งกลับจำนวนองค์ประกอบในสตรีมข้อมูล |
|
อ่านข้อมูลทั้งหมดจากสตรีมและส่งกลับเป็นคอลเล็กชัน |
2. การดำเนินการระดับกลางและปลายทางโดยStream
ชั้นเรียน
อย่างที่คุณเห็น ไม่ใช่ทุกเมธอดในตารางด้านบนที่คืนค่า a Stream
. สิ่งนี้เกี่ยวข้องกับความจริงที่ว่าเมธอดของStream
คลาสสามารถแบ่งออกเป็นเมธอดระดับกลาง (หรือที่เรียกว่าnon-terminal ) และเมธอดเทอร์มินัล
วิธีการระดับกลาง
เมธอดระดับกลางจะส่งคืนออบเจกต์ที่ใช้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);
}
อย่างที่คุณเห็น โค้ดมีขนาดกะทัดรัดกว่ามาก นอกจากนี้ แต่ละบรรทัดของสตรีมไปป์ไลน์ยังเป็นการกระทำเดียว ดังนั้นจึงสามารถอ่านได้เหมือนประโยคในภาษาอังกฤษ:
|
ย้ายจาก a Stream<Owner> เป็น aStream<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>
.
คุณสามารถระบุกฎการกรองได้โดยใช้ฟังก์ชันแลมบ์ดาซึ่งคอมไพลเลอร์จะแปลงเป็นPredicate<T>
ออบเจกต์
ตัวอย่าง:
ลำธารที่ถูกล่ามโซ่ | คำอธิบาย |
---|---|
|
เก็บเฉพาะตัวเลขที่น้อยกว่าสาม |
|
เก็บเฉพาะตัวเลขที่มากกว่าศูนย์ |
Stream<T> sorted(Comparator<T>)
วิธี
เมธอดนี้ส่งคืนสตรีมข้อมูลใหม่ที่จัดเรียงข้อมูลใน สตรี มต้นทาง คุณผ่านตัวเปรียบเทียบซึ่งจะตั้งกฎสำหรับการเปรียบเทียบสององค์ประกอบของสตรีมข้อมูล
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