התור של Java הוא ממשק שזמין בחבילת java.util והמרחיב את ממשק ה- java.util.Collection. דומה לרשימה של Java, התור של Java הוא אוסף של איברים מסודרים (או אובייקטים) אך הוא מבצע פעולות הכנסה והסרה באופן שונה. אנו יכולים להשתמש בתור כדי לאחסן איברים לפני עיבוד האיברים האלה.
תור Java
בסעיף זה, נדון בכמה מהנקודות החשובות על תור Java:
- ממשק java.util.Queue הוא תת-סוג של ממשק java.util.Collection.
- דומה לתור בעולם האמיתי (לדוגמה, בבנק או במכונת ATM), התור מכניס איברים בסופו של התור ומסיר מתחילת התור.
- תור Java מייצג רשימה מסודרת של איברים.
- תור Java עוקב אחר סדר FIFO כדי להכניס ולהסיר את האיברים שלו. FIFO משמעו First In First Out.
- תור Java תומך בכל השיטות של ממשק ה- Collection.
- המימושים הנפוצים ביותר של תור הם LinkedList, ArrayBlockingQueue ו-PriorityQueue.
- BlockingQueues אינם מקבלים איברים null. אם אנו מבצעים פעולה קשורה ל-null, הוא זורק NullPointerException.
- BlockingQueues משמשים למימושי יישומים המבוססים על Producer/Consumer.
- BlockingQueues הם thread-safe.
- התורים שזמינים בחבילת java.util הם תורים לא מוגבלים והתורים שזמינים בחבילת java.util.concurrent הם תורים מוגבלים.
- כל ה-Deques אינם יציבים לתהליכים רציניים.
- ConcurrentLinkedQueue הוא תור לא מוגבל ויציב לתהליכים, מבוסס על חולית מקושרת.
- כל התורים תומכים בהכנסה בזנב התור והוצאה מראש התור, חוץ מ-Deques.
- Deques הם תורים אך הם תומכים בהכנסה והוצאה של אלמנטים משני קצוות.
תרשים המחלקה Queue ב-Java
ממשק התור Queue ב-Java מרחיב את ממשק האוסף Collection. ממשק האוסף Collection מרחיב את ממשק הניתוב Iterable. כמה ממימושי התור הנצפים הם LinkedList, PriorityQueue, ArrayBlockingQueue, DelayQueue, LinkedBlockingQueue, PriorityBlockingQueue ועוד… AbstractQueue מספק יישום סקלטוני של ממשק התור כדי להפחית את המאמץ במימוש התור.
שיטות תור ב-Java
במקטע זה נדבר על כמה מהשיטות השימושיות והנצפות בתורי Java:
- int size(): לקבלת מספר האיברים בקבוצה.
- \begin{hebrew}
שאלה isEmpty(): לבדיקה אם הקבוצה ריקה או לא. - שאלה contains(Object o): מחזירה true אם הקבוצה מכילה את האיבר המסוים.
- Iterator iterator(): מחזירה איטרטור על האיברים בקבוצה. האיברים מוחזרים ללא סדר מסוים.
- שאלה removeAll(Collection c): מסירה מהקבוצה את כל האיברים שנמצאים באוסף הצוין (פעולה אופציונלית).
- שאלה retainAll(Collection c): משאירה רק את האיברים בקבוצה שנמצאים באוסף הצוין (פעולה אופציונלית).
- void clear(): מסירה את כל האיברים מהקבוצה.
- E remove(): Retrieves and removes the head of this queue.
- E poll(): Retrieves and removes the head of this queue, or returns null if this queue is empty.
- E peek(): Retrieves, but does not remove, the head of this queue, or returns null if this queue is empty.
- שאלה offer(E e): מכניסה את האיבר הצוין לתוך התור אם יש אפשרות לעשות זאת מיד מבלי להפר את המגבלות של הקיבולת.
- E element(): Retrieves, but does not remove, the head of this queue.
- שאלה add(E e): מכניסה את האיבר הצוין לתוך התור אם יש אפשרות לעשות זאת מיד מבלי להפר את המגבלות של הקיבולת, מחזירה true בהצלחה וזורקת IllegalStateException אם אין כרגע מקום זמין.
- Object[] toArray(): מחזירה מערך המכיל את כל האיברים בקבוצה. אם בקבוצה יש הבטחות לסדר בו יוחזרו האיברים על ידי האיטרטור שלה, השיטה חייבת להחזיר את האיברים באותו סדר.
יסודים של תור ב-Java</diy12}
\end{hebrew}
כיוון שהתור של ג'אווה מרחיב את ממשק האוסף של ג'אווה, הוא גם תומך בכל הפעולות של ממשק האוסף. בואו נבחן כמה פעולות פשוטות בדוגמה הבאה:
package com.journaldev.queue;
import java.util.*;
public class QueueExample {
public static void main(String[] args) {
Queue queue = new LinkedList<>();
queue.add("one");
queue.add("two");
queue.add("three");
queue.add("four");
System.out.println(queue);
queue.remove("three");
System.out.println(queue);
System.out.println("Queue Size: " + queue.size());
System.out.println("Queue Contains element 'two' or not? : " + queue.contains("two"));
// כדי לרוקן את התור
queue.clear();
}
}
פלט:-
[one, two, three, four]
[one, two, four]
Queue Size: 3
Queue Contains element 'two' or not? : true
מערך ג'אווה לתור
כאן נוכל לבחון כיצד להמיר מערך ג'אווה לתור באמצעות שימוש בשיטת "Collections.addAll()" עם דוגמה פשוטה אחת.
import java.util.*;
public class ArrayToQueue {
public static void main(String[] args) {
String nums[] = {"one","two","three","four","five"};
Queue<String> queue = new LinkedList<>();
Collections.addAll(queue, nums);
System.out.println(queue);
}
}
פלט:- כאשר אנו מריצים את התוכנית לעיל, נקבל את הפלט הבא:
[one, two, three, four, five]
תור ג'אווה למערך
כאן נבחן כיצד להמיר תור ג'אווה למערך ג'אווה באמצעות "toArray()" עם דוגמה פשוטה אחת.
import java.util.*;
public class QueueToArray {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<>();
queue.add("one");
queue.add("two");
queue.add("three");
queue.add("four");
queue.add("five");
String strArray[] = queue.toArray(new String[queue.size()]);
System.out.println(Arrays.toString(strArray));
}
}
פלט:- כאשר אנו מריצים את התוכנית לעיל, נקבל את הפלט הבא:
[one, two, three, four, five]
פעולות משותפות של תור ג'אווה
התור של ג'אווה תומך בכל הפעולות שתומכות בהם ממשק האוסף וגם בכמה פעולות נוספות. הוא תומך בכמעט כל הפעולות בשני צורות.
- אחת מהקבוצות של הפעולות מפילה יוצאת מן הכלל אם הפעולה נכשלת.
- הקבוצה השנייה של הפעולות מחזירה ערך מיוחד אם הפעולה נכשלת.
הטבלה הבאה מסבירה את כל הפעולות הנפוצות בתוך התור בקצרה.
Operation | Throws exception | Special value |
---|---|---|
Insert | add(e) | offer(e) |
Remove | remove() | poll() |
Examine | element() | peek() |
נבחן כל פעולה בנפרד ונדבר עליהן בפרטים עם דוגמאות שימושיות בקטעים הבאים.
פעולות הכנסה לתור ב-Java
בקטע זה, נדבר על פעולת הכנסה לתור ב-Java בפרטים עם דוגמאות שימושיות. אם הפעולה מתבצעת בהצלחה, היא מחזירה ערך "true". כפי שאנו יודעים, תור תומך בפעולת הכנסה בשני צורות:
- Queue.add(e):
הפעולה מפילה יוצאת מן הכלל אם הפעולה נכשלת.- Queue.offer(e):
הפעולה מחזירה ערך מיוחד אם הפעולה נכשלת.
הערה:- הערך המיוחד יכול להיות "false" או "null"
פעולת ההוספה לתור
פעולת ההוספה משמשת להכניס רכיב חדש לתור. אם היא בוצעה בהצלחה, היא מחזירה ערך "true". אחרת, היא זורקת java.lang.IllegalStateException. נפתח דוגמה פשוטה כדי להדגיש את הפונקציונליות שלה.
import java.util.concurrent.*;
public class QueueAddOperation {
public static void main(String[] args) {
BlockingQueue<String> queue = new ArrayBlockingQueue<>(2);
System.out.println(queue.add("one"));
System.out.println(queue.add("two"));
System.out.println(queue);
System.out.println(queue.add("three"));
System.out.println(queue);
}
}
פלט:- כאשר אנו מריצים את התוכנית לעיל, נקבל את הפלט הבא:
true
true
[one, two]
Exception in thread "main" java.lang.IllegalStateException: Queue full
מאחר שהתור שלנו מוגבל לשני איברים, כאשר אנו מנסים להוסיף איבר שלישי באמצעות BlockingQueue.add(), הוא זורק חריגה כפי שמוצג לעיל.
פעולת ההצעה של התור
פעולת ההצעה (offer()) משמשת להכנסת איבר חדש לתור. אם הפעולה הכנסה מבצעת בהצלחה, היא מחזירה את הערך "נכון". אחרת, היא מחזירה את הערך "שקר". בואו נפתח דוגמה פשוטה כדי להדגים את הפונקציונליות הזו.
import java.util.concurrent.*;
public class QueueOfferOperation {
public static void main(String[] args) {
BlockingQueue<String> queue = new ArrayBlockingQueue<>(2);
System.out.println(queue.offer("one"));
System.out.println(queue.offer("two"));
System.out.println(queue);
System.out.println(queue.offer("three"));
System.out.println(queue);
}
}
פלט:- כאשר אנו מריצים את התוכנית לעיל, נקבל את הפלט הבא:
true
true
[one, two]
false
[one, two]
מאחר שהתור שלנו מוגבל לשני איברים, כאשר אנו מנסים להוסיף איבר שלישי באמצעות פעולת BlockingQueue.offer(), היא מחזירה ערך "שקר" כפי שמוצג לעיל.
פעולות מחיקה של התור ב-Java
בסעיף זה, נדון בפעולת המחיקה של התור ב-Java בפרט עם כמה דוגמאות שימושיות. פעולות המחיקה מחזירות את האיבר הראשון בתור, אם הן מבצעות בהצלחה. כפי שאנו יודעים, התור תומך בפעולת מחיקה בשני צורות:
- Queue.remove():
היא זורקת חריגה אם הפעולה נכשלת.- Queue.poll():
היא מחזירה ערך מיוחד אם הפעולה נכשלת.
הערה:- כאן הערך המיוחד יכול להיות או "false" או "null"
פעולת ההסרה מהתור
פעולת ההסרה משמשת למחיקת איבר מראש התור. אם היא מבצעת את פעולת המחיקה בהצלחה, היא מחזירה את האיבר בראש התור. אחרת, היא זורקת java.util.NoSuchElementException. בואו נפתח דוגמה פשוטה כדי להדגיש את הפונקציונליות הזו.
import java.util.*;
public class QueueRemoveOperation
{
public static void main(String[] args)
{
Queue<String> queue = new LinkedList<>();
queue.offer("one");
queue.offer("two");
System.out.println(queue);
System.out.println(queue.remove());
System.out.println(queue.remove());
System.out.println(queue.remove());
}
}
פלט:- כאשר אנו מפעילים את התוכנית למעלה, נקבל את הפלט הבא:
[one, two]
one
two
Exception in thread "main" java.util.NoSuchElementException
כיוון שיש לנו תור רק עם שני איברים, כאשר אנו מנסים לקרוא לשיטה remove() לפעמיים השלישית, היא זורקת חריגה כפי שמוצג למעלה. הערה:- Queue.remove(element) משמש למחיקת איבר מסוים מהתור. אם היא מבצעת את פעולת המחיקה בהצלחה, היא מחזירה ערך "true". אחרת, היא מחזירה ערך "false".
פעולת הקריאה לתור
פעולת הקריאה משמשת למחיקת איבר מראש התור. אם היא מבצעת את פעולת המחיקה בהצלחה, היא מחזירה את האיבר בראש התור. אחרת, היא מחזירה ערך "null". בואו נפתח דוגמה פשוטה כדי להדגיש את הפונקציונליות הזו.
import java.util.*;
public class QueuePollOperation
{
public static void main(String[] args)
{
Queue<String> queue = new LinkedList<>();
queue.offer("one");
queue.offer("two");
System.out.println(queue);
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
}
}
הפלט:- כאשר אנו מפעילים את התוכנית לעיל, נקבל את הפלט הבא:
[one, two]
one
two
null
כיוון שהתור שלנו מכיל רק שני איברים, כאשר אנו מנסים לקרוא לשיטת poll() לפעם השלישית, היא מחזירה ערך ריק כפי שמוצג למעלה.
פעולות בדיקה בתור Java
במקטע זה, נדבר על פעולות בדיקה בתור Java בפרט עם דוגמאות שימושיות. אם הפעולה בוצעה בהצלחה, היא מחזירה את האיבר הראשי של התור בלעדיו. כפי שאנו יודעים, יש לתור פעולת בדיקה בשני צורות:
- Queue.element():
זריקת יוצאת דופן אם הפעולה נכשלת.- Queue.peek():
החזרת ערך מיוחד אם הפעולה נכשלת.
הערה:- כאן ערך מיוחד יכול להיות "שקר" או "ערך ריק"
פעולת Queue.element()
פעולת element() משמשת לקבלת איבר מראש התור, ללא הסרתו. אם היא מבצעת בהצלחה פעולת בדיקה, היא מחזירה את איבר הראש של התור. אחרת, היא זורקת java.util.NoSuchElementException. נפתח דוגמה פשוטה כדי להדגיש את הפונקציונליות הזו.
import java.util.*;
public class QueueElementOperation {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<>();
queue.add("one");
System.out.println(queue.element());
System.out.println(queue);
queue.clear();
System.out.println(queue.element());
}
}
הפלט:- כאשר אנו מפעילים את התוכנית לעיל, נקבל את הפלט הבא:
one
[one]
Exception in thread "main" java.util.NoSuchElementException
אם אנו מנסים לקרוא לשיטת element() על תוך תור ריק, היא זורקת יוצא דופן כמו שמוצג למעלה.
פעולת Queue peek()
פעולת peek() משמשת לאחזור אלמנט מראש התור, בלעדיו הוצאה ממנו. אם הפעולה מבצעת את הבדיקה בהצלחה, היא מחזירה את אלמנט הראש של התור. במקרה אחר, היא מחזירה ערך null. בואו נפתח דוגמה פשוטה כדי להדגיש את הפונקציונליות הזו.
import java.util.*;
public class QueuePeekOperation {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<>();
queue.add("one");
System.out.println(queue.peek());
System.out.println(queue);
queue.clear();
System.out.println(queue.peek());
}
}
פלט:-כאשר אנו מריצים את התוכנית למעלה, נקבל את הפלט הבא:
one
[one]
null
אם ננסה לקרוא לשיטת peek() על תוך תור ריק, היא תחזיר ערך null, אך לא תזרוק יוצא דופן כמו שמוצג למעלה.
קטגוריות Queue ב-Java
ב-Java, ניתן למצוא מספר רב של יישויות Queue. אנו יכולים לסייג אותן לסוגיים הבאים:
- תורים קשורים
- תורים לא מוגבלים
התורים הגבוליים הם תורים שמוגבלים על ידי קיבולת, וכך אנו צריכים לספק את גודל המקסימום של התור בזמן יצירתו. לדוגמה ArrayBlockingQueue (ראה דוגמה קודמת). התורים הלא מוגבלים הם תורים שלא מוגבלים על ידי קיבולת, וכך אנו לא צריכים לספק את גודל התור. לדוגמה LinkedList (ראה דוגמה קודמת). כל התורים הזמינים בחבילת java.util הם תורים לא מוגבלים והתורים הזמינים בחבילת java.util.concurrent הם תורים גבוליים. בדרכים אחרות, אפשר לסווג אותם לכמעט שני סוגים הבאים:
- תורים חוסמים
- תורים לא חוסמים
כל התורים שמיישמים את ממשק BlockingQueue הם תורים חוסמים והשאר הם תורים לא חוסמים. תורים חוסמים חוסמים עד שהם מסיימים את העבודה שלהם או שעבר זמן קצוב, אך תורים לא חוסמים אינם עושים זאת. חלק מהתורים הם תורים מסוג Deques וחלקם הם PriorityQueues.
פעולות בתור חוסם
בנוסף לשני סוגי הפעולות של התור, תור חוסם תומך בשני סוגים נוספים כפי שמוצג למטה.
Operation | Throws exception | Special value | Blocks | Times out |
---|---|---|---|---|
Insert | add(e) | offer(e) | put(e) | offer(e, time, unit) |
Remove | remove() | poll() | take() | poll(time, unit) |
Examine | element() | peek() | N/A | N/A |
כמה מהפעולות נחסמות עד שהן מסיימות את העבודה שלהן ואחרות נחסמות עד שעובר הזמן המוגדר. זו הכל לגבי סקירה מהירה על תור ב-Java. אני מקווה שדוגמאות התור של Java יעזרו לך להתחיל עם תכנות אוסף התורים. אנא השאירו לי תגובה אם אהבתם את המדריכים שלי או יש לכם הצעות או בעיות או שגיאות טכניות. תודה.
Source:
https://www.digitalocean.com/community/tutorials/java-queue