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 |
|---|---|
|
Obyektlar to'plamidan oqim yaratadi |
|
Belgilangan qoida bo'yicha oqim generatsiya qiladi |
|
Birinchi bir nechta oqimlarni birlashtiradi |
|
Ma'lumotlarni filtrlash: faqat belgilangan qoida bilan mos keladigan ma'lumotlarni o'tkazadi |
|
Dublikatlarni olib tashlaydi: allaqachon bor bo'lgan ma'lumotlarni o'tkazmaydi |
|
Ma'lumotlarni saralaydi |
|
Har bir ma'lumotga amal bajaradi |
|
Limmatga yetganda ma'lumotlarni kesadi |
|
Birinchi n ma'lumotlarni o'tkazib yuboradi |
|
Ma'lumotlarni bir turdan boshqasiga o'zgartiradi |
|
Ma'lumotlarni bir turdan boshqasiga o'zgartiradi |
|
Oqimda belgilangan qoida bo'yicha mos keladigan hech bo'lmasa bir ma'lumot borligini tekshiradi |
|
Oqimdagi barcha ma'lumotlar belgilangan qoida bilan mos kelishini tekshiradi |
|
Oqimda hech qaysi ma'lumot belgilangan qoida bilan mos kelmasligini tekshiradi |
|
Qoida bilan mos keladigan birinchi topilgan elementni qaytaradi |
|
Oqimdan qoida bilan mos keladigan biror bir elementni qaytaradi |
|
Oqimdagi minimal elementni qidiradi |
|
Oqimdagi maksimal elementni qaytaradi |
|
Oqimdagi elementlar sonini qaytaradi |
|
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();
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:
|
Stream<Owner>dan Stream<Pet>ga o'tish |
|
oqimda faqat mushuklarni qoldirish |
|
oqimda faqat sariqlarni qoldirish |
|
yoshi kamayish tartibida saralash |
|
ismlarini olish |
|
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 |
|---|---|
|
Faqat uchdan kichik sonlarni qoldirish |
|
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
GO TO FULL VERSION