1. รายการวิธีการของStreamชั้นเรียน

คลาส นี้Streamถูกสร้างขึ้นเพื่อให้ง่ายต่อการสร้างห่วงโซ่ของสตรีมข้อมูล เพื่อให้บรรลุเป้าหมายนี้Stream<T>คลาสมีเมธอดที่ส่งคืนStreamอ็อบเจกต์ ใหม่

สตรีมข้อมูลแต่ละรายการมีการดำเนินการง่ายๆ เพียงอย่างเดียว แต่ถ้าคุณรวมเข้าด้วยกันเป็นสายโซ่และเพิ่มฟังก์ชันแลมบ์ดา ที่น่าสนใจ คุณก็จะมีกลไกที่มีประสิทธิภาพในการสร้างเอาต์พุตที่คุณต้องการ ในไม่ช้าคุณจะเห็นด้วยตัวคุณเอง

นี่คือวิธีการของStreamชั้นเรียน (เฉพาะวิธีพื้นฐานที่สุด):

วิธีการ คำอธิบาย
Stream<T> of()
สร้างกระแสจากชุดของวัตถุ
Stream<T> generate()
สร้างสตรีมตามกฎที่ระบุ
Stream<T> concat()
เชื่อมต่อสองกระแส
Stream<T> filter()
กรองข้อมูล ส่งผ่านเฉพาะข้อมูลที่ตรงกับกฎที่ระบุเท่านั้น
Stream<T> distinct()
ลบรายการที่ซ้ำกัน ไม่ส่งต่อข้อมูลที่พบแล้ว
Stream<T> sorted()
จัดเรียงข้อมูล
Stream<T> peek()
ดำเนินการกับแต่ละองค์ประกอบในสตรีม
Stream<T> limit(n)
ส่งกลับสตรีมที่ถูกตัดให้มีความยาวไม่เกินขีดจำกัดที่ระบุ
Stream<T> skip(n)
ข้ามองค์ประกอบ n ตัวแรก
Stream<R> map()
แปลงข้อมูลจากประเภทหนึ่งไปเป็นอีกประเภทหนึ่ง
Stream<R> flatMap()
แปลงข้อมูลจากประเภทหนึ่งไปเป็นอีกประเภทหนึ่ง
boolean anyMatch()
ตรวจสอบว่ามีองค์ประกอบอย่างน้อยหนึ่งรายการในสตรีมที่ตรงกับกฎที่ระบุหรือไม่
boolean allMatch()
ตรวจสอบว่าองค์ประกอบทั้งหมดในสตรีมตรงกับกฎที่ระบุหรือไม่
boolean noneMatch()
ตรวจสอบว่าไม่มีองค์ประกอบใดในสตรีมที่ตรงกับกฎที่ระบุ
Optional<T> findFirst()
ส่งกลับองค์ประกอบแรกที่พบว่าตรงกับกฎ
Optional<T> findAny()
ส่งกลับองค์ประกอบใด ๆ ในสตรีมที่ตรงกับกฎ
Optional<T> min()
ค้นหาองค์ประกอบขั้นต่ำในสตรีมข้อมูล
Optional<T> max()
ส่งกลับองค์ประกอบสูงสุดในสตรีมข้อมูล
long count()
ส่งกลับจำนวนองค์ประกอบในสตรีมข้อมูล
R collect()
อ่านข้อมูลทั้งหมดจากสตรีมและส่งกลับเป็นคอลเล็กชัน

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);
}

อย่างที่คุณเห็น โค้ดมีขนาดกะทัดรัดกว่ามาก นอกจากนี้ แต่ละบรรทัดของสตรีมไปป์ไลน์ยังเป็นการกระทำเดียว ดังนั้นจึงสามารถอ่านได้เหมือนประโยคในภาษาอังกฤษ:

.flatMap(owner -> owner.getPets().stream())
ย้ายจาก a Stream<Owner>เป็น aStream<Pet>
.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())
ใส่ผลลัพธ์ลงในรายการ

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<Integer> stream = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> stream2 = stream.filter(x -> (x < 3));

เก็บเฉพาะตัวเลขที่น้อยกว่าสาม
Stream<Integer> stream = Stream.of(1, -2, 3, -4, 5);
Stream<Integer> stream2 = stream.filter(x -> (x > 0));

เก็บเฉพาะตัวเลขที่มากกว่าศูนย์

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