1. Stream
tipli metodların siyahısı
Stream
sinfi, məlumat axını zəncirlərini asanlıqla qurmaq üçün yaradılıb. Bunun üçün Stream<T>
tipli obyektin yeni Stream
tipli obyektlər qaytaran metodları var.
Bu məlumat axınlarının hər biri yalnız sadə bir əməliyyat yerinə yetirə bilir, lakin onları birləşdirsək və üstəlik buna lambda-funksiyalar kimi maraqlı bir şeyi əlavə etsək, nəticədə çox güclü bir şey əldə edə bilərik. Tezliklə buna özünüz şahid olacaqsınız.
Budur, Stream
sinfinin əsas metodlarının siyahısı:
Metodlar | Təsvir |
---|---|
|
Obyektlər dəstindən axın yaradır |
|
Müəyyən qaydaya əsasən axın yaradır |
|
Bir neçə axını birləşdirir |
|
Məlumatları süzgəcdən keçirir: yalnız müəyyən qaydaya uyğun məlumatları buraxır |
|
Təkrarlanan məlumatları çıxarır: artıq mövcud olan məlumatları buraxmır |
|
Məlumatları sıralayır |
|
Hər bir məlumat üzərində əməliyyat icra edir |
|
Məlumatları limitdən sonra kəsir |
|
İlk n məlumatı buraxır |
|
Məlumatları bir tipdən digər tipə çevrir |
|
Məlumatları bir tipdən digər tipə çevrir |
|
Axında müəyyən qaydaya uyğun ən az bir məlumatın olduğunu yoxlayır |
|
Axındakı bütün məlumatların müəyyən qaydaya uyğun olduğunu yoxlayır |
|
Axında heç bir məlumatın müəyyən qaydaya uyğun olmadığını yoxlayır |
|
Qaydaya uyğun ilk tapılan elementi qaytarır |
|
Axında qaydaya uyğun istənilən bir elementi qaytarır |
|
Məlumat axınında minimum elementi tapır |
|
Məlumat axınında maksimum elementi qaytarır |
|
Məlumat axınındakı elementlərin sayını qaytarır |
|
Axından bütün məlumatları oxuyur və onları kolleksiya şəklində qaytarır |
2. Intermediate və terminal metodlar Stream
Fərqinə vardığınız kimi, yuxarıdakı cədvəldə göstərilən metodların hamısı Stream
qaytarmır. Bu onunla bağlıdır ki, Stream
sinfinin metodları intermediate (ara, non‑terminal) və terminal (son) olmaq üzrə iki yerə bölünür.
Ara metodlar
Ara metodlar Stream
interfeysini implementasiya edən obyekt qaytarır və onları çağırış zəncirləri şəklində qurmaq olar.
Son metodlar
Son metodlar Stream
tipindən fərqli bir tipdə dəyər qaytarır.
Metod çağırışlarının zənciri
Belə bir sistemlə, hər hansı miqdarda ara metodlardan ibarət zəncirlər qurmaq və sonunda bir terminal metodu çağırmaq mümkündür. Bu yanaşma mürəkkəb loqikanı həyata keçirmək üçün faydalıdır və kodun oxunaqlığını artırır.

Məlumat axınında, məlumatlar ümumiyyətlə dəyişmir. Ara metodlardan ibarət zəncir – məlumatların emal ardıcıllığını göstərən bəyannamə formasında bir yoldur. Bu emal ardıcıllığı yalnız terminal (son) metodu çağırdıqdan sonra həyata keçirilir.
Yəni, terminal metodu çağırılmadan, məlumat axınında heç bir emal baş vermir. Yalnız terminal metodu çağırıldıqdan sonra məlumatlar həmin metod çağırışlarının müəyyən etdiyi qaydalarla emal olunmağa başlayır.
stream()
.intemediateOperation1()
.intemediateOperation2()
...
.intemediateOperationN()
.terminalOperation();
Ara və terminal metodların müqayisəsi:
ara | son | |
---|---|---|
Qaytarılan dəyərin tipi | Stream |
Stream deyil |
Bir neçə metodun bir zəncirdə birləşdirilməsi mümkünlüyü | bəli | xeyr |
Bir zəncirdəki metodların sayı | istənilən | bir dənə çox deyil |
Son nəticə təmin edir | xeyr | bəli |
Axında məlumat emalını işə salır | xeyr | bəli |
Bir misal nəzərdən keçirək.
Heyvansevərlər klubu var. Sabah onların «narıncı pişik günü»dür. Klubda hər birində müxtəlif heyvan siyahısı olan heyvan sahibləri var. Bu, yalnız pişiklər ola bilməz.
Tapşırıq: narıncı pişiklərin adlarını seçmək lazımdır, belə ki, sabah onlar üçün fərdi təbrik açıqcaları hazırlanacaq. Açıqcalar pişiklərin yaşına görə sıralanmalıdır: yaşlıdan cavan pişiyə doğru.
Əvvəlcə bu tapşırığın həlli üçün köməkçi sinifləri təqdim edək:
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;
}
}
İndi isə Selector
sinifinə baxaq, burada yuxarıda göstərilən meyarlara uyğun seçim həyata keçiriləcək:
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 Malaşkov");
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("Dmitri Vasilyev");
owner2.getPets().addAll(List.of(
new Cat("Rızık", Color.FOXY, 7),
new Cat("Barsik", Color.FOXY, 5),
new Parrot("Admiral", Color.BLUE, 3)
));
final Owner owner3 = new Owner("Natiq Kriz");
owner3.getPets().addAll(List.of(
new Dog("Arnold", Color.FOXY, 3),
new Pig("Pilesos", Color.LIGHT_GREY, 8)
));
final Owner owner4 = new Owner("Pavel Murahov");
owner4.getPets().addAll(List.of(
new Snake("Uqav", 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);
}
}
Sadəcə main
metodunun kodunu tamamlamaq qalır, burada əvvəlcə heyvan sahiblərinin siyahısını məlumatlarla dolduracaq initData()
metodu çağırılacaq, sonra narıncı pişiklərin adları seçiləcək və yaşlarına görə azalan qaydada sıralanacaq.
Əvvəlcə bu tapşırığın stream istifadə etməyən koduna baxaq:
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);
}
İndi isə alternativ versiyanı nəzərdən keçirək:
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);
}
Gördüyünüz kimi, kod xeyli kompakt alınıb. Bundan əlavə, stream-in hər sətiri bir hərəkəti göstərir, buna görə onları oxumaq ingiliscə cümlələr kimi mümkündür:
|
Stream<Owner> -dən Stream<Pet> -ə keçid |
|
axında sadəcə pişikləri saxlayırıq |
|
axında sadəcə narıncı rəngliləri saxlayırıq |
|
yaşına görə azalan qaydada sıralayırıq |
|
adları götürürük |
|
nəticəni siyahıya yığırıq |
3. Stream-lərin yaradılması
Stream
sinfinin metodları arasında hələ nəzərdən keçirmədiyimiz üç metod var. Bu üç metodun vəzifəsi - yeni stream-lər yaratmaqdır.
Stream<T>.of(T obj)
Metodu
of()
metodu bir elementdən ibarət bir stream yaradır. Bu, adətən lazımlıdır, məsələn, funksiyanın parametri olaraq Stream<T>
tipində bir obyekt qəbul etməsi tələb olunur, amma sizdə yalnız T
tipində bir obyekt var. O zaman, of()
metodunun köməyi ilə asanlıqla bir stream yarada bilərsiniz, hansı ki, bir elementdən ibarətdir.
Nümunə:
Stream<Integer> stream = Stream.of(1);
Stream<T> Stream.of(T obj1, T obj2, T obj3, ...)
Metodu
of()
metodu göstərilən elementlərdən ibarət bir stream yaradır. Elementlərin sayı istənilən qədər ola bilər. Nümunə:
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
Stream<T> Stream.generate(Supplier<T> obj)
Metodu
generate()
metodu ilə bir qayda təyin edə bilərsiniz, hansı ki, stream-dən növbəti elementin istənilməsi zamanı istifadə olunacaq. Məsələn, hər dəfə təsadüfi bir ədəd qaytarmaq olar.
Nümunə:
Stream<Double> s = Stream.generate(Math::random);
Stream<T> Stream.concat(Stream<T> a, Stream<T> b)
Metodu
concat()
metodu iki göndərilən stream-i birinə birləşdirir. Məlumat oxunan zaman, öncə birinci stream-dən, sonra isə ikinci stream-dən məlumat oxunacaq. Nümunə:
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. Məlumatların filtrasiyası
Bundan başqa, 6 metod yeni məlumat axınları yaradır ki, bu da müxtəlif mürəkkəblikdə axın zəncirlərini birləşdirməyə imkan verir.
Metod Stream<T> filter(Predicate<T>)
Bu metod yeni bir axın qaytarır, bu da mənbə axınındakı məlumatları verilmiş qaydaya uyğun olaraq süzür. Metodu Stream<T> tipli bir obyekt üzərində çağırmaq lazımdır.
Filtrasiya qaydasını təyin etmək üçün lambda-funksiyadan istifadə etmək olar, və bu funksiya kompilator tərəfindən Predicate<T>
tipli bir obyektə çevrilir.
Nümunələr:
Axın zəncirləri | İzah |
---|---|
|
Sadəcə üçdən kiçik ədədləri saxlayırıq |
|
Sadəcə sıfırdan böyük ədədləri saxlayırıq |
Metod Stream<T> sorted(Comparator<T>)
Bu metod, mənbə axınından olan məlumatları sıralayan yeni bir axın qaytarır. Parametr olaraq iki elementin müqayisə qaydasını təyin edən bir Comparator ötürə bilərsiniz.
Metod Stream<T> distinct()
Bu metod yeni bir axın yaradır, bu axın yalnız unikal məlumatları mənbə axınından ehtiva edir. Bütün təkrarlanan məlumatlar atılır. Nümunə:
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
Metod Stream<T> peek(Consumer<T>)
Bu metod yeni bir axın yaradır, lakin buradakı məlumatlar mənbə axınındakı kimi eyni olur. Amma axından növbəti element tələb edildiyi zaman, ona siz peek()
metoduna ötürdüyünüz funksiya çağırılır.
Əgər peek()
metoduna bir funksiya kimi System.out::println
ötürülsə, o halda bütün obyektlər ekranda göstəriləcək, məbləğ axından keçdiyi anda.
Metod Stream<T> limit(int n)
Bu metod, yeni bir axın yaradır, bu axın yalnız ilk n
məlumatlardan ibarət olur mənbə axınından. Bütün digər məlumatlar atılır. Nümunə:
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 2, 2, 2, 3, 4);
Stream<Integer> stream2 = stream.limit(3); // 1, 2, 3
Metod Stream<T> skip(int n)
Bu metod yeni bir axın yaradır, bu axın eyni mənbə axını kimi məlumatlara malikdir, lakin ilk n məlumatları n
görməzlikdən gəlir (keçir). Nümunə:
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