JavaRush /Kurslar /All lectures for UZ purposes /Streamlar bilan ishlash, 1-qism

Streamlar bilan ishlash, 1-qism

All lectures for UZ purposes
Daraja, Dars
Mavjud

1. Stream turidagi metodlar ro'yxati

Stream sinfi ma'lumotlar oqimlarini qurish zanjirini oson yaratish uchun yaratilgan. Shu maqsadda Stream<T> turidagi obyektga yangi Stream turidagi obyektlarni qaytaruvchi metodlar mavjud.

Ushbu ma'lumotlar oqimlarining har biri oddiy bir amalni bajarishga qodir, lekin ularni zanjirga birlashtirish va ularga lambda-funktsiyalarni qo'shish orqali juda kuchli narsa olish mumkin. Tez orada o'zingiz bunga ishonch hosil qilasiz.

Quyidagi metodlar Stream sinfiga tegishli (faqat eng asosiysi):

Metodlar Tavsifi
Stream<T> of()
Obyektlar to'plamidan oqim yaratadi
Stream<T> generate()
Belgilangan qoida bo'yicha oqim generatsiya qiladi
Stream<T> concat()
Birinchi bir nechta oqimlarni birlashtiradi
Stream<T> filter()
Ma'lumotlarni filtrlash: faqat belgilangan qoida bilan mos keladigan ma'lumotlarni o'tkazadi
Stream<T> distinct()
Dublikatlarni olib tashlaydi: allaqachon bor bo'lgan ma'lumotlarni o'tkazmaydi
Stream<T> sorted()
Ma'lumotlarni saralaydi
Stream<T> peek()
Har bir ma'lumotga amal bajaradi
Stream<T> limit(n)
Limmatga yetganda ma'lumotlarni kesadi
Stream<T> skip(n)
Birinchi n ma'lumotlarni o'tkazib yuboradi
Stream<R> map()
Ma'lumotlarni bir turdan boshqasiga o'zgartiradi
Stream<R> flatMap()
Ma'lumotlarni bir turdan boshqasiga o'zgartiradi
boolean anyMatch()
Oqimda belgilangan qoida bo'yicha mos keladigan hech bo'lmasa bir ma'lumot borligini tekshiradi
boolean allMatch()
Oqimdagi barcha ma'lumotlar belgilangan qoida bilan mos kelishini tekshiradi
boolean noneMatch()
Oqimda hech qaysi ma'lumot belgilangan qoida bilan mos kelmasligini tekshiradi
Optional<T> findFirst()
Qoida bilan mos keladigan birinchi topilgan elementni qaytaradi
Optional<T> findAny()
Oqimdan qoida bilan mos keladigan biror bir elementni qaytaradi
Optional<T> min()
Oqimdagi minimal elementni qidiradi
Optional<T> max()
Oqimdagi maksimal elementni qaytaradi
long count()
Oqimdagi elementlar sonini qaytaradi
R collect()
Oqimdan barcha ma'lumotlarni chiqarib oladi va ularni kolleksiyaga qaytaradi

2. Intermediate va terminal Stream metodlari

Ko'rganingizdek, yuqoridagi jadvaldagi barcha metodlar Stream qaytarmaydi. Bu shundaki, Stream sinfi metodlari intermediate (intermediate, non‑terminal) va terminal (terminal) ga bo'linadi.

Intermediate metodlar

Intermediate metodlar Stream interfeysini amalga oshiradigan obyektni qaytaradi va ular metod chaqiruvlarini zanjiriga qo'yilishi mumkin.

Terminal metodlar

Terminal metodlar qaytargan qiymati Stream turidan farq qiladi.

Metod chaqiruvlari zanjiri

Shunday qilib, har qanday miqdorda intermediate metodlardan zanjir yaratishingiz va nihoyat, terminal metodni chaqirishingiz mumkin. Bunday yondashuv murakkab mantiqni amalga oshirish imkonini beradi, shu bilan birga kodni o'qilishi yaxshilanadi.

Ma'lumotlar oqim ichida umuman o'zgarmaydi. Intermediate metodlar zanjiri ma'lumotlarni qayta ishlashning bir qatorini belgilashning hiyla-nayi bir usuli bo'lib, bu terminal metod chaqirilgandan so'ng bajarila boshlaydi.

Ya'ni, terminal metod chaqirilmasdan, ma'lumotlar oqimda hech qanday qayta ishlanmaydi. Va faqat terminal metod chaqirilgandan so'ng, ma'lumotlar zanjirining metodlar chaqiruvi bilan belgilangan qoidalarga ko'ra qayta ishlanadi.

stream()
  .intemediateOperation1()
  .intemediateOperation2()
  ...
  .intemediateOperationN()
  .terminalOperation();
Chaqiruvlar zanjirining umumiy ko'rinishi

Intermediate va terminal metodlarni solishtirish:

intermediate terminal
Qaytarilgan qiymat turi Stream Stream emas
Ushbu turdagi bir nechta metodlarni chaqiruvlar zanjirida birlashtirish imkoniyati ha yo'q
Bitta chaqiruv zanjirida metodlar soni istalgan bir tadan ko'p emas
Yakuniy natijani hosil qiladi yo'q ha
Oqimdagi ma'lumotlarni qayta ishlashni boshlaydi yo'q ha

Keling, misolni ko'rib chiqaylik.

Hayvonlar sevuvchilar klubi mavjud. Ertaga ularda "sariq mushuk kuni". Klubda har birida hayvonlarning ro'yxati mavjud bo'lgan egalar bor. Bu faqat mushuklar bo'lmasligi mumkin.

Vazifa: barcha sariq mushuklarning ismlarini tanlash kerak, ertaga ular uchun "Professional bayram" bilan tabriklovchi otkritkalarni chop etish. Otkritkalar mushuklarning yoshiga ko'ra tartiblangan bo'lishi kerak: katta yoshdagidan kichik yoshdagigacha.

Birinchi navbatda, ushbu vazifani hal qilish uchun yordamchi sinflarni keltiramiz:

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

Endi keltirilgan mezonlar bo'yicha tanlovni amalga oshiradigan Selector sinfini ko'rib chiqamiz:

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("Oleg Malashkov");
      owner1.getPets().addAll(List.of(
            new Cat("Baron", Color.BLACK, 3),
            new Cat("Sultan", Color.DARK_GREY, 4),
            new Dog("Elza", Color.WHITE, 0)
      ));

      final Owner owner2 = new Owner("Dmitriy Vasilkov");
      owner2.getPets().addAll(List.of(
            new Cat("Rijik", Color.FOXY, 7),
            new Cat("Barsik", Color.FOXY, 5),
            new Parrot("Admiral", Color.BLUE, 3)
      ));

      final Owner owner3 = new Owner("Nataliya Krij");
      owner3.getPets().addAll(List.of(
            new Dog("Arnold", Color.FOXY, 3),
            new Pig("Pylesos", Color.LIGHT_GREY, 8)
      ));

      final Owner owner4 = new Owner("Pavel Murahov");
      owner4.getPets().addAll(List.of(
            new Snake("Udav", Color.DARK_GREY, 2)
      ));

      final Owner owner5 = new Owner("Anton Fedorenko");
      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("Zabiaka", Color.DARK_GREY, 1)
      ));

      owners = List.of(owner1, owner2, owner3, owner4, owner5);
   }
}

Main metodini tugatish uchun, initData() metodini chaqiramiz, bu klubdagi hayvonlar egalari ro'yxatini ma'lumotlar bilan to'ldiradi, keyin sariq mushuklar nomlarini tanlaydi va ularni yoshi o'sish tartibida saralaydi.

Birinchi navbatda, streamlardan foydalanmasdan ushbu vazifani hal qilish uchun kodni ko'ramiz:

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

Endi, alternativ variantni ko'rib chiqamiz:

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

Ko'rganingizdek, kod ancha ixcham. Buning ustiga, streamning har bir qatori - bu qandaydir bir amalni bajaradi, shuning uchun ular ingliz tilidagi gaplar kabi o'qilishi mumkin:

.flatMap(owner -> owner.getPets().stream())
Stream<Owner>dan Stream<Pet>ga o'tish
.filter(pet -> Cat.class.equals(pet.getClass()))
oqimda faqat mushuklarni qoldirish
.filter(cat -> Color.FOXY == cat.getColor())
oqimda faqat sariqlarni qoldirish
.sorted((o1, o2) -> o2.getAge() - o1.getAge())
yoshi kamayish tartibida saralash
.map(Animal::getName)
ismlarini olish
.collect(Collectors.toList())
natijani ro'yxatga yig'ish

3. Oqimlarni yaratish

Stream sinfida hali ko'rmagan uchta usul mavjud. Ushbu uchta usulning vazifasi yangi oqimlarni yaratishdir.

Stream<T>.of(T obj) metodi

of() metodi bir elementdan iborat oqim hosil qiladi. Odatda bu, masalan, funksiya Stream<T> turidagi obyektni parametr sifatida qabul qilganda kerak bo'ladi, lekin sizda faqat T turidagi obyekt mavjud. Keyin, of() metodi orqali bir elementdan iborat oqim olish oson va oddiy.

Misol:

Stream<Integer> stream = Stream.of(1);

Stream<T> Stream.of(T obj1, T obj2, T obj3, ...) metodi

of() metodi berilgan elementlardan iborat oqimni hosil qiladi. Elementlar soni har qanday bo'lishi mumkin. Misol:

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);

Stream<T> Stream.generate(Supplier<T> obj) metodi

generate() metodi, agar yangi element oqimidan so'ralganda, uni generatsiya qilish qoidalarini belgilash imkonini beradi. Misol uchun, har safar tasodifiy sonni qaytarish mumkin.

Misol:

Stream<Double> s = Stream.generate(Math::random);

Stream<T> Stream.concat(Stream<T> a, Stream<T> b) metodi

concat() metodi ikki berilgan oqimni bittaga birlashtiradi. Ma'lumotlar birinchi oqimdan keyin ikkinchi oqimdan o'qiladi. Misol:

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. Ma'lumotlarni filtrlash

Yana 6 ta metod yangi ma'lumot oqimlarini yaratadi, bu oqimlarni turli murakkablikdagi zanjirlarga birlashtirish imkonini beradi.

Stream<T> filter(Predicate<T>) metodi

Ushbu metod yangi oqimni qaytaradi, bu oqim manbaidan ma'lumotlarni berilgan qoidaga muvofiq filtrlaydi. Metodni Stream<T> turidagi ob'ektda chaqirish kerak.

Filtrlash qoidasini lambda-funktsiyasi yordamida belgilash mumkin, bu keyin kompilyator tomonidan Predicate<T> turidagi ob'ektga aylantiriladi.

Misollar:

Oqim zanjirlari Tushuntirish
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> stream2 = stream.filter(x -> (x < 3));

Faqat uchdan kichik sonlarni qoldirish
Stream<Integer> stream = Stream.of(1, -2, 3, -4, 5);
Stream<Integer> stream2 = stream.filter(x -> (x > 0));

Faqat noldan katta sonlarni qoldirish

Stream<T> sorted(Comparator<T>) metodi

Bu metod oqim manbaidan ma'lumotlarni saralaydigan yangi oqimni qaytaradi. Parametr sifatida ma'lumotlar oqimidagi ikki elementni taqqoslash qoidalarini belgilaydigan komparator berilishi mumkin.

Stream<T> distinct() metodi

Bu metod yangi oqimni qaytaradi, bu faqat unikallarni o'z ichiga oladi manba ma'lumotlar oqimidan. Barcha dublikatlar chiqarib tashlanadi. Misol:

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>) metodi

Ushbu metod yangi oqimni qaytaradi, garchi undagi ma'lumotlar manba oqimidagi kabi bo'lsa ham. Lekin, oqimdan navbatdagi element so'ralganda, siz peek() metodiga bergan funksiya unga chaqiriladi.

Agar peek() metodiga System.out::println funksiyasini bersangiz, barcha obyektlar oqimdan o'tib ketayotganda ekranga chiqariladi.

Stream<T> limit(int n) metodi

Bu metod yangi oqimni qaytaradi, bu faqat birinchi n ma'lumotlarni manba oqimidan o'z ichiga oladi. Boshqa barcha ma'lumotlar chiqarib tashlanadi. Misol:

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) metodi

Bu metod yangi oqimni qaytaradi, bu manba oqimidan barcha bir xil ma'lumotlarni o'z ichiga oladi, lekin birinchi n ma'lumotlarni o'tkazib yuboradi. Misol:

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

Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION