מדריך לממשק Java BiFunction

1. הקדמה

Java 8 הציג תכנות בסגנון פונקציונלי, מה שמאפשר לנו לבצע פרמטר של שיטות למטרות כלליות על ידי העברת פונקציות.

אנו כנראה מכירים את ממשקי Java 8 עם פרמטר יחיד פוּנקצִיָה, לְבַסֵס, ו צרכן.

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

2. פונקציות של פרמטר יחיד

בואו נסכם במהירות את האופן שבו אנו משתמשים בפונקציה פרמטר יחיד או unary, כפי שאנו עושים בזרמים:

רשימה ממופה = Stream.of ("שלום", "עולם") .מפה (מילה -> מילה + "!") .Collect (Collectors.toList ()); assertThat (ממופה). מכיל בדיוק ("שלום!", "עולם!");

כפי שאנו רואים, מַפָּה שימושים פוּנקצִיָה, שלוקח פרמטר יחיד ומאפשר לנו לבצע פעולה על ערך זה, ולהחזיר ערך חדש.

3. פעולות דו פרמטריות

ספריית Java Stream מספקת לנו לְהַפחִית פונקציה המאפשרת לנו לשלב את האלמנטים של זרם. עלינו לבטא כיצד הופכים הערכים שצברנו עד כה על ידי הוספת הפריט הבא.

ה לְהַפחִית פונקציה משתמשת בממשק הפונקציונלי BinaryOperator, שלוקח שני אובייקטים מאותו סוג כמו הקלטים שלו.

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

3.1. שימוש במבדה

יישום למבדה עבור א BiFunction קודמת לשני פרמטרים, מוקפים בסוגריים:

תוצאת מחרוזת = Stream.of ("שלום", "עולם") .פחת ("", (a, b) -> b + "-" + a); assertThat (תוצאה) .isEqualTo ("עולם שלום-");

כפי שאנו רואים, שני הערכים, א ו ב הם מיתרים. כתבנו למבדה שמשלבת ביניהם כדי להפיק את התפוקה הרצויה, עם השנייה הראשונה, ונקף בין לבין.

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

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

תוצאת מחרוזת = Stream.of ("שלום", "עולם") .פחת ("", (מחרוזת a, מחרוזת b) -> b + "-" + a);

3.2. שימוש בפונקציה

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

מחרוזת פרטית combineWithoutTrailingDash (מחרוזת a, מחרוזת b) {אם (a.isEmpty ()) {return b; } להחזיר b + "-" + a; }

ואז קוראים לזה:

תוצאת מחרוזת = Stream.of ("שלום", "עולם") .reduce ("", (a, b) -> combineWithoutTrailingDash (a, b)); assertThat (תוצאה). isEqualTo ("עולם שלום");

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

3.3. באמצעות הפניה לשיטה

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

בואו נכתוב את הקוד שלנו כדי להשתמש בהתייחסות לשיטה:

תוצאה מחרוזת = Stream.of ("שלום", "עולם") .reduce ("", זה :: combineWithoutTrailingDash); assertThat (תוצאה). isEqualTo ("עולם שלום");

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

4. שימוש BiFunction

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

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

רשימת רשימה 1 = Arrays.asList ("a", "b", "c"); רשימת רשימה 2 = Arrays.asList (1, 2, 3); תוצאת רשימה = ArrayList חדש (); עבור (int i = 0; i <list1.size (); i ++) {result.add (list1.get (i) + list2.get (i)); } assertThat (תוצאה). contains בדיוק ("a1", "b2", "c3");

4.1. הכללת הפונקציה

אנו יכולים להכליל פונקציה מיוחדת זו באמצעות a BiFunction כמשלב:

רשימת סטטי פרטית listCombiner (רשימת רשימה 1, רשימה רשימה 2, שילוב BiFunction) {תוצאת רשימה = ArrayList חדש (); עבור (int i = 0; i <list1.size (); i ++) {result.add (combiner.apply (list1.get (i), list2.get (i))); } להחזיר תוצאה; }

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

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

4.2. קורא לפונקציה הכללית

המשלב שלנו הוא א BiFunction, המאפשר לנו להזריק אלגוריתם, לא משנה מה סוגי הקלט והפלט. בואו ננסה את זה:

רשימת רשימה 1 = Arrays.asList ("a", "b", "c"); רשימת רשימה 2 = Arrays.asList (1, 2, 3); תוצאת רשימה = listCombiner (list1, list2, (a, b) -> a + b); assertThat (תוצאה). מכיל בדיוק ("a1", "b2", "c3");

ואנחנו יכולים להשתמש בזה גם לסוגים שונים לחלוטין של כניסות ויציאות.

בואו נזריק אלגוריתם כדי לקבוע אם הערך ברשימה הראשונה גדול מהערך בשנייה ונפיק a בוליאני תוֹצָאָה:

רשימת רשימה 1 = Arrays.asList (1.0d, 2.1d, 3.3d); רשימת רשימה 2 = Arrays.asList (0.1f, 0.2f, 4f); תוצאת רשימה = listCombiner (list1, list2, (a, b) -> a> b); assertThat (תוצאה). מכיל בדיוק (נכון, נכון, שקר);

4.3. א BiFunction הפניה לשיטה

בוא נשכתב את הקוד שלעיל בשיטה שחולצה ובהפניה לשיטה:

רשימת רשימה 1 = Arrays.asList (1.0d, 2.1d, 3.3d); רשימת רשימה 2 = Arrays.asList (0.1f, 0.2f, 4f); תוצאת רשימה = listCombiner (רשימה 1, רשימה 2, זה :: firstIsGreaterThanSecond); assertThat (תוצאה). מכיל בדיוק (נכון, נכון, שקר); פרטית בוליאנית firstIsGreaterThanSecond (כפול a, Float b) {להחזיר a> b; }

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

4.4. BiFunction הפניות לשיטה באמצעות זֶה

בואו נדמיין שאנחנו רוצים להשתמש באמור לעיל BiFunction-אלגוריתם מבוסס כדי לקבוע אם שתי רשימות שוות:

רשימת רשימה 1 = Arrays.asList (0.1f, 0.2f, 4f); רשימת רשימה 2 = Arrays.asList (0.1f, 0.2f, 4f); תוצאת רשימה = listCombiner (list1, list2, (a, b) -> a.equals (b)); assertThat (תוצאה). מכיל בדיוק (נכון, נכון, נכון);

אנו יכולים למעשה לפשט את הפתרון:

תוצאת רשימה = listCombiner (list1, list2, Float :: שווה);

הסיבה לכך היא ש שווים לתפקד ב לָצוּף יש אותה חתימה כמו a BiFunction. נדרש פרמטר ראשון מרומז של זֶה, אובייקט מסוג לָצוּף. הפרמטר השני, אַחֵר, מהסוג לְהִתְנַגֵד, הוא הערך להשוואה.

5. לחן BiFunctions

מה אם נוכל להשתמש בהפניות לשיטה כדי לעשות את אותו הדבר כמו דוגמת השוואת הרשימה המספרית שלנו?

רשימת רשימה 1 = Arrays.asList (1.0d, 2.1d, 3.3d); רשימת רשימה 2 = Arrays.asList (0.1d, 0.2d, 4d); תוצאת רשימה = listCombiner (list1, list2, Double :: CompareTo); assertThat (תוצאה). מכיל בדיוק (1, 1, -1);

זה קרוב לדוגמא שלנו אבל מחזיר מספר שלם, ולא המקורי בוליאני. הסיבה לכך היא ש בהשוואה ל שיטה ב לְהַכפִּיל החזרות מספר שלם.

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

לאחר מכן, בואו ניצור פונקציה שתכריח את הפניה לשיטה שלנו כפול :: השווה לתוך BiFunction:

BiFunction סטטי פרטי asBiFunction (פונקציית BiFunction) {פונקציית החזרה; }

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

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

רשימת רשימה 1 = Arrays.asList (1.0d, 2.1d, 3.3d); רשימת רשימה 2 = Arrays.asList (0.1d, 0.2d, 4d); תוצאת רשימה = listCombiner (list1, list2, asBiFunction (כפול :: השווה ל-). ואז (i -> i> 0)); assertThat (תוצאה). מכיל בדיוק (נכון, נכון, שקר);

6. מסקנה

במדריך זה חקרנו BiFunction ו BinaryOperator במונחים של ספריית Java Streams המסופקת והפונקציות המותאמות אישית שלנו. בדקנו איך לעבור BiFunctions באמצעות lambdas והפניות לשיטה, וראינו כיצד לחבר פונקציות.

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

כמו תמיד, דוגמאות הקוד המלאות זמינות ב- GitHub.