מדריך לקיבוץ Java 8 מאת Collector

1. הקדמה

במדריך זה, נראה איך קיבוץ לפי אספן עובד באמצעות דוגמאות שונות.

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

2. קיבוץ לפי אספנים

ג'אווה 8 זרם API מאפשר לנו לעבד אוספי נתונים בצורה הצהרתית.

שיטות המפעל הסטטיות Collectors.groupingBy () ו Collectors.groupingByConcurrent () לספק לנו פונקציונליות דומה ל- 'GROUP BY ' סעיף בשפת SQL. אנו משתמשים בהם לקיבוץ אובייקטים לפי מאפיין כלשהו ושמירת תוצאות ב- מַפָּה למשל.

השיטות העמוסות מדי של קיבוץ לפי הם:

  • ראשית, עם פונקציית סיווג כפרמטר השיטה:

אספן סטטי<>> groupingBy (סיווג פונקציות)
  • שנית, עם פונקציית סיווג ואספן שני כפרמטרים של שיטה:

אספן סטטי groupingBy (סיווג פונקציות, אספן במורד הזרם)
  • לבסוף, עם פונקציית סיווג, שיטת ספק (המספקת את מַפָּה יישום המכיל את התוצאה הסופית), ואספן שני כפרמטרים של שיטה:

סטָטִי  קיבוץ אספנים לפי (מסווג פונקציות, map mapfactory, אספן במורד הזרם)

2.1. הגדרת קוד לדוגמא

כדי להדגים את השימוש ב- groupingBy ()בואו נגדיר א פוסט בבלוג בכיתה (נשתמש בזרם של פוסט בבלוג חפצים):

class BlogPost {כותרת מחרוזת; מחבר מחרוזת; סוג BlogPostType; int אוהב; } 

לאחר מכן, BlogPostType:

enum BlogPostType {NEWS, סקירה, מדריך} 

אז ה רשימה שֶׁל פוסט בבלוג חפצים:

הודעות ברשימה = Arrays.asList (...);

בואו נגדיר גם א טופל בכיתה שישמש לקבץ פוסטים על ידי שילוב שלהם סוּג ו מְחַבֵּר תכונות:

class Tuple {סוג BlogPostType; מחבר מחרוזת; } 

2.2. קיבוץ פשוט לפי עמודה אחת

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

לקבץ את פוסטי הבלוג ברשימת הפוסטים לפי הבלוגים שלהם סוּג:

מַפָּה postsPerType = posts.stream () .collect (groupingBy (BlogPost :: getType)); 

2.3. קיבוץ לפי עם מתחם מַפָּה סוג מפתח

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

לקבץ פוסטים בבלוג ברשימה לפי סוּג ו מְחַבֵּר משולב ב טופל למשל:

מַפָּה postsPerTypeAndAuthor = posts.stream () .collect (groupingBy (post -> Tuple new (post.getType (), post.getAuthor ()))); 

2.4. שינוי המוחזר מַפָּה סוג ערך

העומס השני של קיבוץ לפי לוקח אספן שני נוסף (אספן במורד הזרם) המיושם על תוצאותיו של האספן הראשון.

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

בואו נשתמש ב- כדי להגדיר() אספן כאספן במורד הזרם וקבל א מַעֲרֶכֶת של פוסטים בבלוג (במקום א רשימה):

מַפָּה postsPerType = posts.stream () .collect (groupingBy (BlogPost :: getType, toSet ())); 

2.5. קיבוץ לפי מספר שדות

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

לקבץ את רשימה שֶׁל פוסט בבלוגהראשון על ידי מְחַבֵּר ואז עד סוּג:

מַפָּה map = posts.stream () .collect (groupingBy (BlogPost :: getAuthor, groupingBy (BlogPost :: getType)));

2.6. קבלת הממוצע מתוצאות מקובצות

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

למשל, כדי למצוא את המספר הממוצע של אוהב לכל פוסט בבלוג סוּג:

מפה averageLikesPerType = posts.stream () .collect (groupingBy (BlogPost :: getType, averagingInt (BlogPost :: getLikes))); 

2.7. קבלת הסכום מתוצאות מקובצות

לחישוב הסכום הכולל של אוהב לכל אחד סוּג:

מפה likesPerType = posts.stream () .collect (groupingBy (BlogPost :: getType, summingInt (BlogPost :: getLikes))); 

2.8. קבלת מקסימום או מינימום מתוצאות מקובצות

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

מַפָּה maxLikesPerPostType = posts.stream () .collect (groupingBy (BlogPost :: getType, maxBy (comparingInt (BlogPost :: getLikes)))); 

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

שים לב שה- maxBy ו minBy אספנים לוקחים בחשבון את האפשרות שהאוסף עליו הם מוחלים יכול להיות ריק. זו הסיבה שסוג הערך במפה הוא אופציונאלי.

2.9. קבלת סיכום למאפיין של תוצאות מקובצות

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

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

מפה כמוStatisticsPerType = posts.stream () .collect (groupingBy (BlogPost :: getType, summarizingInt (BlogPost :: getLikes))); 

ה IntSummaryStatistics האובייקט לכל סוג מכיל את הספירה, הסכום, הממוצע, הערך המינימלי והמקסימלי עבור אוהב תְכוּנָה. אובייקטים סיכומים נוספים קיימים לערכים כפולים וארוכים.

2.10. מיפוי תוצאות מקובצות לסוג אחר

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

בואו נקשר לשרשור כותרתשל ההודעות עבור כל פוסט בבלוג סוּג:

מפה postsPerType = posts.stream () .collect (groupingBy (BlogPost :: getType, mapping (BlogPost :: getTitle, joining (",", "כותרות פוסט: [", "]")))); 

מה שעשינו כאן זה למפות כל אחד מהם פוסט בבלוג למשל לשלה כותרת ואז צמצם את זרם כותרות ההודעה לשרשור חוּט. בדוגמה זו, סוג ה- מַפָּה הערך שונה גם מברירת המחדל רשימה סוּג.

2.11. שינוי השיבה מַפָּה סוּג

בעת השימוש ב- קיבוץ לפי אספן, איננו יכולים להניח הנחות לגבי סוג המוחזר מַפָּה. אם אנחנו רוצים להיות ספציפיים לגבי איזה סוג של מַפָּה אנחנו רוצים להגיע מהקבוצה עד אז נוכל להשתמש בווריאציה השלישית של קיבוץ לפי שיטה המאפשרת לנו לשנות את סוג ה- מַפָּה על ידי העברת a מַפָּה פונקציית ספק.

בואו נחזור EnumMap על ידי העברת EnumMap פונקצית הספק ל קיבוץ לפי שיטה:

EnumMap postsPerType = posts.stream () .collect (groupingBy (BlogPost :: getType, () -> EnumMap חדש (BlogPostType.class), toList ())); 

3. במקביל קיבוץ לפי אַסְפָן

דומה ל קיבוץ לפי האם ה groupingByConcurrent אספן, הממנף ארכיטקטורות מרובות ליבות. לאספן זה יש שלוש שיטות עמוסות שלוקחות את אותם טיעונים בדיוק כמו השיטות העמוסות בהתאמה של קיבוץ לפי אַסְפָן. סוג ההחזרה של ה- groupingByConcurrent אספן, לעומת זאת, חייב להיות מופע של ConcurrentHashMap כיתה או תת-מחלקה שלה.

כדי לבצע פעולת קיבוץ במקביל, הזרם צריך להיות מקביל:

ConcurrentMap postsPerType = posts.parallelStream () .collect (groupingByConcurrent (BlogPost :: getType)); 

אם נבחר להעביר א מַפָּה פונקצית הספק ל groupingByConcurrent אספן, אז עלינו לוודא שהפונקציה מחזירה או ConcurrentHashMap או תת-מחלקה שלו.

4. תוספות Java 9

Java 9 הציגה שני אספנים חדשים שעובדים היטב איתם קיבוץ לפי; מידע נוסף עליהם ניתן למצוא כאן.

5. מסקנה

במאמר זה בחנו את השימוש ב- קיבוץ לפי אספן שמציע ג'אווה 8 אספנים ממשק API.

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

היישום המלא של הדוגמאות במאמר זה נמצא בפרויקט GitHub.