מבוא לספליטרטור בג'אווה

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

ה ספליטור ממשק, שהוצג ב- Java 8, יכול להיות משמש לחציית רצפים ומחיצות. זה כלי בסיס עבור זרמים, במיוחד מקבילים.

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

2. ספליטור ממשק API

2.1. נסה להתקדם

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

כאן, נבחן כיצד להשתמש בו לחציית אלמנטים ומחיצות.

ראשית, נניח שיש לנו רשימת מערך עם 35000 מאמרים וזה מאמר מחלקה מוגדרת כ:

מאמר בכיתה ציבורית {List list privateOfAuthors; מזהה פרטי פרטי; שם מחרוזת פרטי; // בונים סטנדרטיים / גטרים / סטרים}

עכשיו בואו נבצע משימה שמעבדת את רשימת המאמרים ומוסיפה סיומת של "- פורסם על ידי Baeldung ” לכל שם מאמר:

שיחת מחרוזת ציבורית () {int current = 0; בעוד (spliterator.tryAdvance (a -> a.setName (article.getName () .concat ("- פורסם על ידי Baeldung")))) {הנוכחי ++; } להחזיר Thread.currentThread (). getName () + ":" + הנוכחי; }

שים לב שמשימה זו מפיקה את מספר המאמרים שעובדו כאשר היא מסיימת את הביצוע.

נקודת מפתח נוספת היא שהשתמשנו בה tryAdvance () שיטה לעיבוד האלמנט הבא.

2.2. trySplit

הבא, בואו נתפצל ספליטורים (ומכאן השם) ולעבד מחיצות באופן עצמאי.

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

בואו ניצור תחילה את הרשימה שלנו:

רשימה סטטית ציבורית genererenElements () {החזר Stream.generate (() -> מאמר חדש ("Java")) .limit (35000) .collect (Collectors.toList ()); }

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

@Test הציבור בטל givenSpliterator_whenAppliedToAListOfArticle_thenSplittedInHalf () {ספליטרטור פיצול 1 = Executor.generateElements (). ספליטרטור (); ספליטרטור split2 = split1.trySplit (); assertThat (משימה חדשה (split1) .call ()) .containsSequence (Executor.generateElements (). size () / 2 + ""); assertThat (משימה חדשה (split2) .call ()) .containsSequence (Executor.generateElements (). size () / 2 + ""); }

תהליך הפיצול עבד כמתוכנן וחילק את הרשומות באופן שווה.

2.3. estimatedSize

ה estimatedSize השיטה נותנת לנו מספר מוערך של אלמנטים:

LOG.info ("גודל:" + split1.estimateSize ());

זה יפיק:

גודל: 17500

2.4. יש מאפיינים

ממשק API זה בודק אם המאפיינים הנתונים תואמים למאפייני ה- ספליטור. ואז אם נפעיל את השיטה לעיל, הפלט יהיה int ייצוג של מאפיינים אלה:

LOG.info ("מאפיינים:" + split1.characteristics ());
מאפיינים: 16464

3. ספליטור מאפיינים

יש לו שמונה מאפיינים שונים המתארים את התנהגותו. אלה יכולים לשמש רמזים לכלים חיצוניים:

  • גודל אם הוא מסוגל להחזיר מספר מדויק של אלמנטים עם ה- estimateSize () שיטה
  • מְמוּיָן - אם זה חוזר דרך מקור ממוין
  • ממוצע - אם נחלק את המופע באמצעות a trySplit () שיטה ולקבל ספליטורים שהם גודל גם כן
  • זרם - אם ניתן לשנות את המקור בבטחה במקביל
  • מוּבהָק - אם לכל זוג אלמנטים שנתקלים בהם x, y,! x.equals (y)
  • בלתי ניתן לשינוי - אם לא ניתן לשנות מבנים אלמנטים המוחזקים על ידי מקור
  • לא - אם המקור מחזיק באפסים או לא
  • הוזמן - אם חזר על רצף מסודר

4. מותאם אישית ספליטור

4.1. מתי להתאים אישית

ראשית, נניח את התרחיש הבא:

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

שֶׁלָנוּ מְחַבֵּר הכיתה תיראה כך:

מחלקה ציבורית מחבר {שם פרטי מחרוזת; פרטי int relatedArticleId; // סטנדרטים, סטרים ובונים סטנדרטיים}

בשלב הבא נבצע כיתה לספירת מחברים תוך כדי חציית זרם מחברים. לאחר מכן הכיתה תבצע הפחתה על הנחל.

בואו נסתכל על יישום הכיתה:

מחלקה ציבורית RelatedAuthorCounter {counter counter int; פרטי בוליאנית קשורה; // בונים סטנדרטיים / גטרים ציבוריים צבורים RelatedAuthorCounter (מחבר מחבר) {if (author.getRelatedArticleId () == 0) {return isRelated? זה: RelatedAuthorCounter חדש (נגד, נכון); } אחר {החזרה קשורה? RelatedAuthorCounter חדש (מונה + 1, שקר): זה; }} צירוף ציבורי RelatedAuthorCounter (RelatedAuthorCounter RelatedAuthorCounter) {החזר RelatedAuthorCounter חדש (מונה + RelatedAuthorCounter.counter, RelatedAuthorCounter.isRelated); }}

כל שיטה בכיתה לעיל מבצעת פעולה ספציפית לספירה תוך כדי חצייה.

קודם ה לִצְבּוֹר() שיטה חוצה את המחברים בזה אחר זה באופן איטרטיבי, לאחר מכן לְשַׁלֵב() מסכם שני דלפקים המשתמשים בערכים שלהם. סוף - סוף, ה getCounter () מחזיר את הדלפק.

עכשיו, כדי לבדוק מה עשינו עד כה. בואו להמיר את רשימת המחברים של המאמר שלנו לזרם מחברים:

זרם זרם = article.getListOfAuthors (). Stream ();

וליישם א countAuthor () שיטה לביצוע ההפחתה בזרם באמצעות RelatedAuthorCounter:

פרטי int countAutors (זרם זרם) {RelatedAuthorCounter wordCounter = stream.reduce (חדש RelatedAuthorCounter (0, נכון), RelatedAuthorCounter :: לצבור, RelatedAuthorCounter :: לשלב); להחזיר wordCounter.getCounter (); }

אם השתמשנו בזרם רציף הפלט יהיה כצפוי "ספירה = 9"אולם הבעיה מתעוררת כאשר אנו מנסים להקביל את הפעולה.

בואו נסתכל על מקרה הבדיקה הבא:

@Test בטל givenAStreamOfAuthors_whenProcessedInParallel_countProducesWrongOutput () {assertThat (Executor.countAutors (stream.parallel ())). IsGreaterThan (9); }

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

4.2. כיצד להתאים אישית

כדי לפתור את זה, אנחנו צריכים ליישם א ספליטור שמפצל מחברים רק כאשר הם קשורים תְעוּדַת זֶהוּת ו ArticleId התאמות. הנה היישום של המנהג שלנו ספליטור:

מחלקה ציבורית RelatedAuthorSpliterator מיישמת ספליטרטור {רשימת רשימות סופיות פרטיות; AtomicInteger הנוכחי = AtomicInteger חדש (); // קונסטרוקטור סטנדרטי / גטרס @ Override בוליאני ציבורי tryAdvance (פעולת צרכנים) {action.accept (list.get (current.getAndIncrement ())); להחזיר current.get () <list.size (); } @ Splideator ציבורי @ Override trySplit () {int currentSize = list.size () - current.get (); אם (currentSize <10) {return null; } עבור (int splitPos = currentSize / 2 + current.intValue (); splitPos <list.size (); splitPos ++) {if (list.get (splitPos) .getRelatedArticleId () == 0) {ספליטרטור ספליטרטור = חדש RelatedAuthorSpliterator ( list.subList (current.get (), splitPos)); current.set (splitPos); מפצל חזרה; }} להחזיר null; } @Override פומבי ארוך estimSize () {return list.size () - current.get (); } @ מאפייני int public public () {להחזיר CONCURRENT; }}

עכשיו פונה countAuthors () השיטה תתן את הפלט הנכון. הקוד הבא מדגים כי:

@ מבחן חלל ציבורי givenAStreamOfAuthors_whenProcessedInParallel_countProducesRightOutput () {Stream stream2 = StreamSupport.stream (ספליטרטור, נכון); assertThat (Executor.countAutors (stream2.parallel ())). isEqualTo (9); }

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

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

  • נסה להתקדם מעביר מחברים ל צרכן במיקום המדד הנוכחי ומגדיל את מיקומו
  • trySplit מגדיר את מנגנון הפיצול, במקרה שלנו, את RelatedAuthorSpliterator נוצר כאשר מתאימים מזהים, והפיצול מחלק את הרשימה לשני חלקים
  • estimatedSize - הוא ההבדל בין גודל הרשימה למיקומו של הכותב המחודש
  • מאפיינים- מחזיר את ספליטור מאפיינים, במקרה שלנו גודל כערך שהוחזר על ידי estimatedSize () השיטה היא מדויקת; יתר על כך, זרם מציין כי המקור לכך ספליטור עשוי להשתנות בבטחה על ידי שרשורים אחרים

5. תמיכה בערכים פרימיטיביים

ה ספליטורממשק API תומך בערכים פרימיטיביים כולל לְהַכפִּיל, int ו ארוך.

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

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

  • של פרימיטיבי: ממשק הורה לפרימיטיבים אחרים
  • OfInt: א ספליטור התמחה עבור int
  • OfDouble: א ספליטור מוקדש ל לְהַכפִּיל
  • OfLong: א ספליטור מוקדש ל ארוך

6. מסקנה

במאמר זה סקרנו את Java 8 ספליטור שימוש, שיטות, מאפיינים, תהליך פיצול, תמיכה פרימיטיבית ואופן ההתאמה האישית.

כמו תמיד, היישום המלא של מאמר זה ניתן למצוא באתר Github.


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