מדריך ל- TreeSet בג'אווה

1. סקירה כללית

במאמר זה נסתכל על חלק בלתי נפרד ממסגרת אוספי Java ו- אחד הפופולריים ביותר מַעֲרֶכֶת יישומים - TreeSet.

2. מבוא ל TreeSet

במילים פשוטות, ה TreeSet הוא אוסף ממוין המרחיב את תקציר סט בכיתה ומיישם את ניווט ניווט מִמְשָׁק.

הנה סיכום מהיר של ההיבטים החשובים ביותר ביישום זה:

  • הוא מאחסן אלמנטים ייחודיים
  • זה לא שומר על סדר ההכנסה של האלמנטים
  • זה ממיין את האלמנטים בסדר עולה
  • זה לא בטוח בחוטים

ביישום זה ממוינים אובייקטים ומאוחסנים בסדר עולה בהתאם לסדרם הטבעי. ה TreeSet משתמש בעץ חיפוש בינארי בעל איזון עצמי, ליתר דיוק a אדום שחור עֵץ.

במילים פשוטות, בהיותו עץ חיפוש בינארי בעל איזון עצמי, כל צומת של העץ הבינארי כולל ביט נוסף המשמש לזיהוי צבע הצומת שהוא אדום או שחור. במהלך הכנסות ומחיקות שלאחר מכן, פיסות "צבע" אלה מסייעות להבטיח שהעץ נשאר מאוזן פחות או יותר.

אז בואו ניצור מופע של TreeSet:

הגדר treeSet = חדש TreeSet ();

2.1. TreeSet עם Parameter Comparator Comparator

באופן אופציונלי, אנו יכולים לבנות א TreeSet עם בנאי המאפשר לנו להגדיר את סדר מיון האלמנטים באמצעות a ניתן להשוות אוֹ משווה:

הגדר treeSet = חדש TreeSet (Comparator.comparing (מחרוזת :: אורך));

למרות ש TreeSet אינו בטוח בחוטים, ניתן לסנכרן אותו חיצוני באמצעות ה- Collections.synchronizedSet () עֲטִיפָה:

הגדר syncTreeSet = Collections.synchronizedSet (treeSet);

בסדר, עכשיו שיש לנו מושג ברור כיצד ליצור TreeSet למשל, בואו נסתכל על הפעולות הנפוצות שיש לנו.

3. TreeSetלְהוֹסִיף()

ה לְהוֹסִיף() ניתן להשתמש בשיטה, כצפוי, להוספת אלמנטים ל- a TreeSet. אם נוסף אלמנט, השיטה חוזרת נָכוֹן, אחרת - שֶׁקֶר.

החוזה של השיטה קובע כי אלמנט יתווסף רק כאשר אותו כבר לא נמצא ב- מַעֲרֶכֶת.

בואו נוסיף אלמנט ל- a TreeSet:

@Test ציבורי בטל כאשרAddingElement_shouldAddElement () {Set treeSet = New TreeSet (); assertTrue (treeSet.add ("מחרוזת נוספה")); }

ה לְהוֹסִיף שיטה חשובה ביותר מכיוון שפרטי היישום של השיטה ממחישים כיצד TreeSet עובד באופן פנימי, איך זה ממנף את TreeMap'sלָשִׂים שיטה לאחסון האלמנטים:

הוסף בוליאני ציבורי (E e) {return m.put (e, PRESENT) == null; }

המשתנה M מתייחס לגיבוי פנימי TreeMap (ציין זאת TreeMap מכשירים מפה ניווטת):

מעבר זמני NavigableMap m;

לכן, ה TreeSet פנימי תלוי בגיבוי NavigableMap שמתאחל עם מופע של TreeMap כאשר מופע של TreeSet נוצר:

TreeSet ציבורי () {this (TreeMap חדש ()); }

מידע נוסף על כך ניתן למצוא במאמר זה.

4. TreeSet מכיל ()

ה מכיל () משתמשים בשיטה כדי לבדוק אם אלמנט נתון קיים בנתון TreeSet. אם האלמנט נמצא, הוא מחזיר נכון, אחרת שֶׁקֶר.

בוא נראה את מכיל () בִּפְעוּלָה:

@Test ציבורי בטל כאשרCheckingForElement_shouldSearchForElement () {Set treeSetContains = new TreeSet (); treeSetContains.add ("מחרוזת נוספה"); assertTrue (treeSetContains.contains ("מחרוזת נוספה")); }

5. TreeSet להסיר ()

ה לְהַסִיר() נעשה שימוש בשיטה להסרת האלמנט שצוין מהסט אם הוא קיים.

אם קבוצה הכילה את האלמנט שצוין, שיטה זו תחזור נָכוֹן.

בואו נראה את זה בפעולה:

@Test ציבורי בטל כאשר RemovingElement_shouldRemoveElement () {Set removeFromTreeSet = New TreeSet (); removeFromTreeSet.add ("מחרוזת נוספה"); assertTrue (removeFromTreeSet.remove ("מחרוזת נוספה")); }

6. TreeSet נקה ()

אם אנו רוצים להסיר את כל הפריטים מערכה, נוכל להשתמש ב- ברור() שיטה:

@Test ציבורי בטל כאשרClearingTreeSet_shouldClearTreeSet () {Set clearTreeSet = New TreeSet (); clearTreeSet.add ("מחרוזת נוספה"); clearTreeSet.clear (); assertTrue (clearTreeSet.isEmpty ()); }

7. גודל TreeSet ()

ה גודל() השיטה משמשת לזיהוי מספר האלמנטים הקיימים ב- TreeSet. זו אחת השיטות הבסיסיות ב- API:

@Test ציבורי בטל כאשרCheckingTheSizeOfTreeSet_shouldReturnThesize () {Set treeSetSize = New TreeSet (); treeSetSize.add ("מחרוזת נוספה"); assertEquals (1, treeSetSize.size ()); }

8. TreeSet הוא ריק ()

ה זה ריק() ניתן להשתמש בשיטה כדי להבין אם נתון TreeSet מופע ריק או לא:

@Test ציבורי בטל כאשרCheckingForEmptyTreeSet_shouldCheckForEmpty () {Set emptyTreeSet = New TreeSet (); assertTrue (emptyTreeSet.isEmpty ()); }

9. איטרטור TreeSet ()

ה איטרטור () שיטה מחזירה איטרטור החוזר בסדר עולה מעל האלמנטים ב- מַעֲרֶכֶת. איטרטורים אלה אינם מהירים.

אנו יכולים לצפות בסדר האיטרציה העולה כאן:

@Test ציבורי בטל כאשרIteratingTreeSet_shouldIterateTreeSetInAscendingOrder () {Set treeSet = new TreeSet (); treeSet.add ("ראשון"); treeSet.add ("שני"); treeSet.add ("שלישי"); Iterator itr = treeSet.iterator (); בעוד (itr.hasNext ()) {System.out.println (itr.next ()); }}

בנוסף, TreeSet מאפשר לנו לחזור באמצעות מַעֲרֶכֶת בסדר יורד.

בואו נראה את זה בפעולה:

@Test ציבורי בטל כאשרIteratingTreeSet_shouldIterateTreeSetInDescendingOrder () {TreeSet treeSet = חדש TreeSet (); treeSet.add ("ראשון"); treeSet.add ("שני"); treeSet.add ("שלישי"); Iterator itr = treeSet.descendingIterator (); בעוד (itr.hasNext ()) {System.out.println (itr.next ()); }}

ה איטרטור זורק א ConcurrentModificationException iאם הסט משתנה בכל עת לאחר יצירת האיטרטור בכל דרך שהיא למעט באמצעות האיטרטור לְהַסִיר() שיטה.

בואו ליצור מבחן לכך:

@Test (צפוי = ConcurrentModificationException.class) בטל בציבור כאשרModifyingTreeSetWhileIterating_shouldThrowException () {Set treeSet = new TreeSet (); treeSet.add ("ראשון"); treeSet.add ("שני"); treeSet.add ("שלישי"); Iterator itr = treeSet.iterator (); בעוד (itr.hasNext ()) {itr.next (); treeSet.remove ("שני"); }} 

לחלופין, אם היינו משתמשים בשיטת ההסרה של איטרטור, לא היינו נתקלים בחריג:

@Test הציבור בטל כאשר RemovingElementUsingIterator_shouldRemoveElement () {Set treeSet = new TreeSet (); treeSet.add ("ראשון"); treeSet.add ("שני"); treeSet.add ("שלישי"); Iterator itr = treeSet.iterator (); בעוד (itr.hasNext ()) {אלמנט מחרוזת = itr.next (); if (element.equals ("Second")) itr.remove (); } assertEquals (2, treeSet.size ()); }

אין ערובה להתנהגות מהירה כושלת של איטרטור מכיוון שאי אפשר לבצע שום ערבויות קשות בנוכחות שינוי מקביל לא מסונכרן.

עוד על כך ניתן למצוא כאן.

10. TreeSet ראשון ()

שיטה זו מחזירה את האלמנט הראשון מ- a TreeSet אם זה לא ריק. אחרת, זה זורק א NoSuchElementException.

בואו נראה דוגמה:

@Test ציבורי בטל כאשרCheckingFirstElement_shouldReturnFirstElement () {TreeSet treeSet = New TreeSet (); treeSet.add ("ראשון"); assertEquals ("First", treeSet.first ()); }

11. TreeSet האחרון ()

אנלוגית לדוגמא לעיל, שיטה זו תחזיר את האלמנט האחרון אם הסט אינו ריק:

@Test ציבורי בטל כאשרCheckingLastElement_shouldReturnLastElement () {TreeSet treeSet = New TreeSet (); treeSet.add ("ראשון"); treeSet.add ("אחרון"); assertEquals ("Last", treeSet.last ()); }

12. TreeSet subSet ()

שיטה זו תחזיר את האלמנטים הנעים בין fromElement ל toElement. ציין זאת fromElement הוא כולל ו toElement הוא בלעדי:

@ מבחן פומבי בטל כאשר משתמשSubSet_shouldReturnSubSetElements () {SortedSet treeSet = סט עץ חדש (); treeSet.add (1); treeSet.add (2); treeSet.add (3); treeSet.add (4); treeSet.add (5); treeSet.add (6); הגדר expectSet = TreeSet חדש (); expectSet.add (2); expectSet.add (3); expectSet.add (4); expectSet.add (5); הגדר subSet = treeSet.subSet (2, 6); assertEquals (expectSet, subSet); }

13. TreeSet headSet ()

שיטה זו תחזיר אלמנטים של TreeSet שהם קטנים מהאלמנט שצוין:

@ מבחן פומבי בטל כאשר UsHeadSet_shouldReturnHeadSetElements () {SortedSet treeSet = סט עץ חדש (); treeSet.add (1); treeSet.add (2); treeSet.add (3); treeSet.add (4); treeSet.add (5); treeSet.add (6); הגדר subSet = treeSet.headSet (6); assertEquals (subSet, treeSet.subSet (1, 6)); }

14. TreeSet tailSet ()

שיטה זו תחזיר את האלמנטים של a TreeSet שגדולים או שווים לאלמנט שצוין:

@Test ציבורי בטל כאשרUsingTailSet_shouldReturnTailSetElements () {NavigableSet treeSet = סט עץ חדש (); treeSet.add (1); treeSet.add (2); treeSet.add (3); treeSet.add (4); treeSet.add (5); treeSet.add (6); הגדר subSet = treeSet.tailSet (3); assertEquals (subSet, treeSet.subSet (3, true, 6, true)); }

15. אחסון ריק אלמנטים

לפני Java 7, ניתן היה להוסיף ריק אלמנטים לריק TreeSet.

עם זאת, זה נחשב לבאג. לָכֵן, TreeSet כבר לא תומך בתוספת ריק.

כאשר אנו מוסיפים אלמנטים ל- TreeSet, האלמנטים ממוינים לפי הסדר הטבעי שלהם או כמפורט על ידי משווה. מכאן להוסיף א ריק, בהשוואה לאלמנטים קיימים, תוצאות א NullPointerException מאז ריק לא ניתן להשוות לשום ערך:

@Test (צפוי = NullPointerException.class) בטל בציבור כאשר AdddingNullToNonEmptyTreeSet_shouldThrowException () {Set treeSet = new TreeSet (); treeSet.add ("ראשון"); treeSet.add (null); }

אלמנטים מוכנסים לתוך TreeSet חייב ליישם את ניתן להשוות ממשק או לפחות להתקבל על ידי המשווה שצוין. כל האלמנטים הללו חייבים להיות דומים זה לזה,כְּלוֹמַרe1.compareTo (e2) אוֹ comparator.compare (e1, e2)אסור לזרוק א ClassCastException.

בואו נראה דוגמה:

אלמנט בכיתה {מזהה שלם פרטי; // שיטות אחרות ...} משווה השוואה = (ele1, ele2) -> {return ele1.getId (). CompareTo (ele2.getId ()); }; @ מבחן פומבי בטל כאשר UsComparator_shouldSortAndInsertElements () {Set treeSet = New TreeSet (comparator); אלמנט ele1 = אלמנט חדש (); ele1.setId (100); אלמנט ele2 = אלמנט חדש (); ele2.setId (200); treeSet.add (ele1); treeSet.add (ele2); System.out.println (treeSet); }

16. ביצוע של TreeSet

בהשוואה לא HashSet הביצועים של א TreeSet נמצא בצד התחתון. פעולות כמו לְהוֹסִיף, לְהַסִיר ו לחפש לקחת O (יומן n) זמן תוך פעולות כמו הדפסה נ אלמנטים לפי סדר מיון דורשים עַל) זְמַן.

א TreeSet צריכה להיות הבחירה העיקרית שלנו אם אנו רוצים לשמור על ערכינו מסודרים כ- TreeSet ניתן לגשת ולחצות אותם בסדר עולה או יורד, וביצועי פעולות ותצוגות עולות עלולים להיות מהירים יותר מאלה של ירידות.

עקרון היישוב - הוא מונח לתופעה בה מגיעים לעתים קרובות לאותם ערכים, או למיקומי אחסון קשורים, בהתאם לדפוס הגישה לזיכרון.

כשאנחנו אומרים יישוב:

  • לעתים קרובות גישה לאפליקציה בתדירות דומה מגישה לנתונים דומים
  • אם שתי רשומות נמצאות בקרבת מקום בהזמנה, א TreeSet ממקם אותם זה ליד זה במבנה הנתונים, ומכאן בזיכרון

א TreeSet בהיותנו מבנה נתונים עם יישוב גדול יותר אנו יכולים, אם כן, להסיק בהתאם לעקרון היישוב, כי עלינו לתת עדיפות ל TreeSet אם חסר לנו הזיכרון ואם אנו רוצים לגשת לאלמנטים שקרובים יחסית זה לזה לפי הסדר הטבעי שלהם.

במקרה שצריך לקרוא נתונים מהכונן הקשיח (שיש לו זמן אחזור גדול יותר מאשר לקרוא נתונים מהמטמון או מהזיכרון) אז מעדיפים TreeSet כיוון שיש לו יישוב גדול יותר

17. מסקנה

במאמר זה אנו מתמקדים בהבנת השימוש בתקן TreeSet יישום ב- Java. ראינו את מטרתו ועד כמה היא יעילה בכל הנוגע לשימושיות בהתחשב ביכולתה למנוע כפילויות ולמיין אלמנטים.

כמו תמיד, ניתן למצוא קטעי קוד ב- GitHub.


$config[zx-auto] not found$config[zx-overlay] not found