1. Queue interfeysi: klassik növbə (FIFO)
Proqramlaşdırmada, həyatda olduğu kimi, bəzən təkcə elementləri saxlamaq deyil, onların emal qaydasını idarə etmək vacibdir. Supermarketdə növbəni təsəvvür edin: kim başlanğıca daha yaxındırsa — daha tez xidmət alır. Bu, FIFO prinsipidir — “birinci gələn — birinci gedən”. Şkafdakı boşqab yığını isə LIFO prinsipi ilə işləyir — “son gələn — ilk gedən”. Java-da bu prinsiplərə koleksiyalar cavab verir: növbələr Queue, iki uclu növbələr Deque və yığınlar Stack.
Harada tətbiq olunur:
- Tapşırıqların daxilolma qaydasında emalı (tapşırıq növbəsi, sənədlərin çapı, hadisələrin emalı).
- Əməllərin ləğvi (undo/redo) — yığın.
- Ifadələrin parsinqi, ağac və qrafların keçilməsi (eninə/dərinliyinə axtarış) və daha çox.
Növbə nədir?
Növbə (Queue) — FIFO prinsipi ilə qurulmuş kolleksiyadır. Elementlər sonuna əlavə olunur və başlanğicdan çıxarılır. Mağazada olduğu kimi: kim daha tez gəlibsə, ilk olaraq o xidmət alır.
Queue interfeysinin əsas metodları
| Metod | Nə edir | Qaytarır |
|---|---|---|
|
Elementi sonuna əlavə edir (istisnasız) | true/false |
|
Elementi sonuna əlavə edir (istisna atır) | true/istisna |
|
Birinci elementi silir və qaytarır | element/null |
|
Birinci elementi silir və qaytarır | element/istisna |
|
Birinci elementi silmədən qaytarır | element/null |
|
Birinci elementi silmədən qaytarır | element/istisna |
Oxşar davranışlı cüt metodları gördünüz. Fərq “yumşaqlıq”dadır: offer, poll, peek uğursuzluqda xüsusi dəyər qaytarır (adətən null və ya false) və istisna atmır, add, remove, element isə sərhəd vəziyyətlərində (boş növbə, bəzən də dolma) istisna atır.
Nümunə: tapşırıqlar növbəsi
import java.util.*;
public class QueueDemo {
public static void main(String[] args) {
Queue<String> tasks = new LinkedList<>();
tasks.offer("Dişləri fırçalamaq");
tasks.offer("İdman etmək");
tasks.offer("Qəhvə içmək");
while (!tasks.isEmpty()) {
String task = tasks.poll(); // Tapşırığı növbənin əvvəlindən götürürük
System.out.println("İcra edirəm: " + task);
}
}
}
Çıxış:
İcra edirəm: Dişləri fırçalamaq
İcra edirəm: İdman etmək
İcra edirəm: Qəhvə içmək
Niyə çox vaxt LinkedList istifadə olunur?
İnterfeys — müqavilədir və bir neçə reallaşdırması var. Tez-tez LinkedList istifadə olunur, çünki hər iki sondan sürətli əlavə/silmə edir. Lakin əla alternativ — ArrayDeque-dur. Xüsusiləşdirilmiş variantlar da var: PriorityQueue (prioritetlərə görə növbə), bu barədə aşağıda.
2. Deque interfeysi: iki uclu növbə
Deque (Double Ended Queue, “dek”) — elementləri hər iki tərəfdən: həm əvvəlindən, həm də sonundan əlavə edib silmək mümkün olan növbədir. Sanki iki qapılı avtobus: daxil olub-çıxmaq hər iki tərəfdən mümkündür.
Deque = universal əsgər: adi növbə (FIFO), yığın (LIFO) və hibrid kimi işləyə bilər.
Deque interfeysinin əsas metodları
| Metod | Nə edir | İstifadə nümunəsi |
|---|---|---|
|
Əvvəlinə əlavə et | |
|
Sonuna əlavə et | |
|
Əvvəldən sil və qaytar | |
|
Sondan sil və qaytar | |
|
Əvvələ bax (silmədən) | |
|
Sona bax (silmədən) | |
|
Əvvəlinə əlavə et (istisnasız) | |
|
Sonuna əlavə et (istisnasız) | |
Nümunə: Deque həm növbə, həm yığın kimi
import java.util.*;
public class DequeDemo {
public static void main(String[] args) {
Deque<String> deque = new ArrayDeque<>();
// Növbə kimi istifadə edirik (FIFO)
deque.offerLast("A");
deque.offerLast("B");
deque.offerLast("C");
System.out.println("Növbə (FIFO):");
while (!deque.isEmpty()) {
System.out.println(deque.pollFirst());
}
// Yığın kimi istifadə edirik (LIFO)
deque.offerLast("1");
deque.offerLast("2");
deque.offerLast("3");
System.out.println("Yığın (LIFO):");
while (!deque.isEmpty()) {
System.out.println(deque.pollLast());
}
}
}
Çıxış:
Növbə (FIFO):
A
B
C
Yığın (LIFO):
3
2
1
Niyə ArrayDeque?
ArrayDeque — Deque-in massiv üzərində sürətli və sıx reallaşdırmasıdır. Yığın və növbə üçün o, adətən köhnə Stack-dən daha sürətli və etibarlıdır, və sabit ölçüsü yoxdur (yalnız yaddaşla məhdudlaşır).
3. Stack: yığın — son daxil olan, ilk çıxır (LIFO)
Yığın — LIFO prinsipi ilə işləyən kolleksiyadır: üst element son əlavə edilən olur. Proqramlaşdırmada yığın əməl tarixçəsi (undo), rekursiv strukturların (ağaclar/qraflar) keçilməsi, ifadələrin hesablanması və s. üçün faydalıdır.
Stack sinfi (və niyə ondan istifadə etməmək lazımdır)
Java-da Stack sinfi var və o, köhnəlmiş Vector-dan miras alır. Bu gün yeni layihələrdə ondan istifadə tövsiyə edilmir. Daha üstün seçim — Deque/ArrayDeque.
Yığın metodları (Deque)
| Metod | Nə edir |
|---|---|
|
Elementi yığının üstünə qoy |
|
Üst elementı götür və qaytar |
|
Üst elementə bax |
Diqqət: Deque-də bu əməliyyatlar addFirst/removeFirst/peekFirst metodlarına uyğundur, lakin uyğunluq üçün push/pop/peek “yığın” adları da mövcuddur.
Nümunə: ArrayDeque üzərində yığın
import java.util.*;
public class StackDemo {
public static void main(String[] args) {
Deque<String> stack = new ArrayDeque<>();
stack.push("Birinci");
stack.push("İkinci");
stack.push("Üçüncü");
while (!stack.isEmpty()) {
System.out.println(stack.pop());
}
}
}
Çıxış:
Üçüncü
İkinci
Birinci
Niyə Stack deyil?
- Stack sinxronizasiya olunub (daha yavaşdır) və köhnəlmiş Vector-dan miras alır.
- ArrayDeque çox vaxt daha sürətli və daha sıxdır, axınları bloklamır.
- Yeni kodda Stack görsəniz — adətən o, ArrayDeque ilə əvəzlənməli namizəddir.
4. Nə zaman və nə istifadə etməli
| Struktur | Prinsip | Harada istifadə etmək | Reallaşdırma nümunəsi |
|---|---|---|---|
| Queue | FIFO | Tapşırıqlar növbəsi, hadisələrin emalı, çap, müştəri növbələri | |
| Stack | LIFO | Undo/redo, rekursiya, parserlər, ağacların keçilməsi | |
| Deque | FIFO/LIFO | Bufferlər, palindromlar, iki uclu növbələr, universal tapşırıqlar | |
Həyatdan nümunələr:
- Bankda növbə — Queue.
- Redaktorda əməl tarixçəsi — Stack.
- Avtobus növbəsi, hər iki tərəfdən giriş/çıxış — Deque.
5. Kod nümunələri: mini-tətbiqdə növbə və yığını reallaşdıraq
Nümunə 1: Sənədlərin çap növbəsi
import java.util.*;
public class PrintQueueApp {
public static void main(String[] args) {
Queue<String> printQueue = new ArrayDeque<>();
printQueue.offer("Sənəd1.pdf");
printQueue.offer("Sənəd2.docx");
printQueue.offer("Sənəd3.xls");
while (!printQueue.isEmpty()) {
String doc = printQueue.poll();
System.out.println("Çap olunur: " + doc);
}
}
}
Nümunə 2: Əməllərin ləğv yığını (undo)
import java.util.*;
public class UndoStackApp {
public static void main(String[] args) {
Deque<String> undoStack = new ArrayDeque<>();
undoStack.push("Mətni daxil etmə");
undoStack.push("Rəngin dəyişdirilməsi");
undoStack.push("Şəklin silinməsi");
System.out.println("Son əməl: " + undoStack.peek()); // Şəklin silinməsi
while (!undoStack.isEmpty()) {
System.out.println("Geri al: " + undoStack.pop());
}
}
}
Nümunə 3: İki uclu növbə
import java.util.*;
public class DequeApp {
public static void main(String[] args) {
Deque<String> deque = new ArrayDeque<>();
deque.addFirst("A");
deque.addLast("B");
deque.addFirst("C");
deque.addLast("D");
System.out.println("Əvvəldən çıxarırıq: " + deque.removeFirst()); // C
System.out.println("Sondan çıxarırıq: " + deque.removeLast()); // D
System.out.println("Qaldı: " + deque); // [A, B]
}
}
6. Reallaşdırma xüsusiyyətləri və nüanslar
- ArrayDeque — əksər tapşırıqlar üçün növbə/yığın reallaşdırması olaraq ən sürətli və ən universal seçimdir.
- LinkedList — həmçinin Deque-i reallaşdırır; faydalı ola bilər, lakin qısa növbələr üçün adətən ArrayDeque-dən daha yavaşdır.
- PriorityQueue — prioritetə görə növbədir: qayda prioritetlə (təbii və ya Comparator vasitəsilə) müəyyən olunur, bu, adi FIFO-növbə deyil.
- Stack — köhnəlmiş və sinxronlaşdırılmışdır; yeni layihələrdə ArrayDeque üstünlük təşkil edir.
- Deque həm əvvəl, həm də sonla işləməyə imkan verdiyi üçün növbə və yığın ssenarilərini tək abstraksiya ilə əhatə edir.
7. Növbələr və yığınlarla işləyərkən tipik səhvlər
Səhv №1: Deque əvəzinə Stack-dən istifadə.
Bir çox yeni başlayanlar Stack sinfini adı görə seçirlər. Müasir layihələrdə onun yerinə ArrayDeque (və ya LinkedList) üzərində push/pop metodları istifadə olunur.
Səhv №2: Strukturun prinsipini pozmaq.
Növbə/yığın elementlərinə indekslə müraciət etməyə çalışırlar, məsələn queue.get(0) və ya stack.get(0). Belə olmaz — növbə və yığın üçün uyğun metodlardan istifadə edin (peek, poll, pop və s.).
Səhv №3: remove()/element()/add() yoxlamasız istifadə.
remove, element, add metodları boş/dolmuş strukturda istisna ata bilər. Uğursuzluqda xüsusi dəyər qaytaran “yumşaq” offer, poll, peek metodları ilə işləmək daha təhlükəsizdir.
Səhv №4: ArrayDeque-də null elementlərdən istifadə.
ArrayDeque null qəbul etmir. null əlavə etmək cəhdi NullPointerException-a səbəb olacaq.
Səhv №5: PriorityQueue-nu adi növbə kimi istifadə etmək.
PriorityQueue-da qayda prioritetlərlə müəyyən edilir (təbii və ya Comparator vasitəsilə). Sərt FIFO üçün ArrayDeque və ya LinkedList istifadə edin.
Səhv №6: for-each ilə keçərkən növbə/yığını dəyişmək.
for-each zamanı kolleksiyadan element silmək ConcurrentModificationException-a gətirib çıxara bilər. Silmə üçün iterator istifadə edin və ya nümunələrdə göstərildiyi kimi dövrdə poll/pop metodlarından istifadə edin.
GO TO FULL VERSION