השוואת אובייקטים בג'אווה

1. הקדמה

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

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

2. == ו != מפעילים

נתחיל עם == ו != אופרטורים שיכולים לדעת אם שני אובייקטים של Java זהים או לא, בהתאמה.

2.1. פרימיטיבים

עבור סוגים פרימיטיביים, להיות זהה פירושו להיות בעלי ערכים שווים:

assertThat (1 == 1) .isTrue ();

תודה לביטול האגרוף האוטומטי, זה עובד גם כאשר משווים ערך פרימיטיבי עם מקבילו מסוג העטיפה:

שלם a = מספר שלם חדש (1); assertThat (1 == a) .isTrue ();

אם לשני מספרים שלמים יש ערכים שונים, ה- == מפעיל יחזור שֶׁקֶר, בזמן ש != מפעיל יחזור נָכוֹן.

2.2. חפצים

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

שלם a = מספר שלם חדש (1); מספר שלם b = מספר שלם חדש (1); assertThat (a == b) .isFalse ();

על ידי השוואה בין שני אובייקטים, הערך של אובייקטים אלה אינו 1. אלא כתובות הזיכרון שלהם בערימה שונים מכיוון ששני האובייקטים נוצרו באמצעות ה- חָדָשׁ מַפעִיל. אם היינו מקצים א ל באז הייתה לנו תוצאה אחרת:

שלם a = מספר שלם חדש (1); מספר שלם ב = a; assertThat (a == b) .isTrue ();

בואו נראה מה קורה כשאנחנו משתמשים ב- מספר שלם # valueOf שיטת מפעל:

מספר שלם a = Integer.valueOf (1); מספר שלם b = Integer.valueOf (1); assertThat (a == b) .isTrue ();

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

ג'אווה עושה זאת גם בשביל חוּט:

assertThat ("שלום!" == "שלום!"). זה נכון ();

עם זאת, אם הם נוצרו באמצעות חָדָשׁ מפעיל, אז הם לא יהיו אותו הדבר.

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

assertThat (null == null) .isTrue (); assertThat ("שלום!" == null) .isFalse ();

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

3. אובייקט # שווה שיטה

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

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

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

שלם a = מספר שלם חדש (1); מספר שלם b = מספר שלם חדש (1); assertThat (א. שווה (ב)). isTrue ();

השיטה עדיין חוזרת נָכוֹן כששני האובייקטים זהים.

עלינו לציין שאנחנו יכולים לעבור א ריק אובייקט כטיעון השיטה, אך כמובן, לא כאובייקט שאליו אנו קוראים השיטה.

אנחנו יכולים להשתמש ב- שווים() שיטה עם אובייקט משלנו. בוא נגיד שיש לנו a אדם מעמד:

אדם בכיתה ציבורית {שם פרטי פרטי מחרוזת; שם משפחה פרטי מחרוזת; אדם ציבורי (שם מחרוזת, שם משפחה מחרוזת) {this.firstName = שם פרטי; this.lastName = lastname; }}

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

@ עקוף בוליאני ציבורי שווה (אובייקט o) אם (זה == o) יחזיר נכון; אם (o == null 

למידע נוסף, עיין במאמרנו בנושא זה.

4. אובייקטים # שווים שיטה סטטית

בואו נסתכל עכשיו על אובייקטים # שווים שיטה סטטית. הזכרנו קודם שאנחנו לא יכולים להשתמש ריק כערך האובייקט הראשון אחרת א NullPointerException ייזרק.

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

בואו נשווה אדם חפצים שוב עם:

Person joe = אדם חדש ("ג'ו", "פורטמן"); אדם joeAgain = אדם חדש ("ג'ו", "פורטמן"); אדם נטלי = אדם חדש ("נטלי", "פורטמן"); assertThat (Objects.equals (joe, joeAgain)). isTrue (); assertThat (Objects.equals (joe, natalie)). isFalse ();

כמו שאמרנו, השיטה מטפלת ריק ערכים. לכן, אם שני הטיעונים הם ריק זה יחזור נָכוֹן, ואם רק אחד מהם הוא ריק, זה יחזור שֶׁקֶר.

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

אדם ציבורי (שם מחרוזת, שם משפחה מחרוזת, תאריך לידה תאריך) {זה (שם פרטי, שם משפחה); this.birthDate = birthDate; }

ואז נצטרך לעדכן את שווים() שיטה אבל עם ריק טיפול. נוכל לעשות זאת על ידי הוספת תנאי זה לתנאינו שווים() שיטה:

birthDate == null? that.birthDate == null: birthDate.equals (that.birthDate);

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

Objects.equals (birthDate, that.birthDate);

5. ניתן להשוות מִמְשָׁק

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

ה ניתן להשוות הממשק הוא כללי ויש לו רק שיטה אחת, בהשוואה ל(), שלוקח טיעון מהסוג הגנרי ומחזיר int. הערך המוחזר הוא שלילי אם זֶה נמוך מהוויכוח, 0 אם הם שווים, וחיובי אחרת.

בואו נגיד, שלנו אדם בכיתה, אנחנו רוצים להשוות אדם חפצים לפי שם המשפחה שלהם:

יישומי אדם בכיתה ציבורית השוואה {// ... @Override public int CompareTo (Person o) {להחזיר this.lastName.compareTo (o.lastName); }}

ה בהשוואה ל() השיטה תחזיר שלילי int אם נקרא עם אדם בעל שם משפחה גדול מזה זֶה, אפס אם אותו שם משפחה, וחיובי אחרת.

למידע נוסף, עיין במאמר שלנו בנושא זה.

6. משווה מִמְשָׁק

ה משווה הממשק הוא כללי ויש לו לְהַשְׁווֹת שיטה שלוקחת שני טיעונים מאותו סוג גנרי ומחזירה an מספר שלם. את הדפוס הזה כבר ראינו קודם עם ה- ניתן להשוות מִמְשָׁק.

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

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

בואו ניצור אדםמשווה שישווה אותם רק בשמות הפרטיים שלהם:

Comparator CompareByFirstNames = Comparator.comparing (אדם :: getFirstName);

בואו עכשיו למיין א רשימה של אנשים שמשתמשים בזה משווה:

Person joe = אדם חדש ("ג'ו", "פורטמן"); אדם allan = אדם חדש ("אלן", "דייל"); רשימת אנשים = ArrayList חדש (); אנשים. להוסיף (ג'ו); אנשים.תוסיפו (אלן); people.sort (CompareByFirstNames); טוענים כי (אנשים). מכיל בדיוק (אלן, ג'ו);

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

@Override int int publicTo (Person o) {return Comparator.comparing (Person :: getLastName) .thenComparing (Person :: getFirstName) .thenComparing (Person :: getBirthDate, Comparator.nullsLast (Comparator.naturalOrder ())). זה, o); }

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

7. אפאצ'י קומונס

בואו נסתכל כעת בספריית Apache Commons. קודם כל, בואו נייבא את התלות של Maven:

 org.apache.commons commons-lang3 3.10 

7.1. ObjectUtils # notEqual שיטה

ראשית, בואו נדבר על ה ObjectUtils # notEqual שיטה. זה לוקח שניים לְהִתְנַגֵד טיעונים, כדי לקבוע אם הם לא שווים, על פי שלהם שווים() יישום שיטה. זה גם מטפל ריק ערכים.

בואו נעשה שימוש חוזר שלנו חוּט דוגמאות:

מחרוזת a = מחרוזת חדשה ("שלום!"); מחרוזת b = מחרוזת חדשה ("שלום עולם!"); assertThat (ObjectUtils.notEqual (a, b)). isTrue (); 

צריך לציין ש ObjectUtils יש לו שווים() שיטה. עם זאת, זה הוצא משימוש מאז Java 7, מתי אובייקטים # שווים הופיע

7.2. ObjectUtils # השווה שיטה

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

בוא נראה את זה באמצעות מיתרים שוב:

מחרוזת ראשונה = מחרוזת חדשה ("שלום!"); מחרוזת שנייה = מחרוזת חדשה ("מה שלומך?"); assertThat (ObjectUtils.compare (ראשון, שני)). הוא שלילי ();

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

8. גויאבה

עכשיו, בואו נסתכל על גויאבה. קודם כל, בואו נייבא את התלות:

 com.google.guava גויאבה 29.0-jre 

8.1. אובייקטים שווים # שיטה

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

מחרוזת a = מחרוזת חדשה ("שלום!"); מחרוזת b = מחרוזת חדשה ("שלום!"); assertThat (Objects.equal (a, b)). isTrue ();

אף על פי שהוא אינו מסומן כמוצא משימוש, ה- JavaDoc בשיטה זו אומר שיש לראותו כמיושן מכיוון שג'אווה 7 מספקת את אובייקטים # שווים שיטה.

8.2. שיטות השוואה

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

assertThat (Ints.compare (1, 2)). is Negative ();

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

8.3. ComparisonChain מעמד

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

אדם נטלי = אדם חדש ("נטלי", "פורטמן"); Person joe = אדם חדש ("ג'ו", "פורטמן"); int comparisonResult = ComparisonChain.start (). להשוות (natalie.getLastName (), joe.getLastName ()). להשוות (natalie.getFirstName (), joe.getFirstName ()) .תוצאה (); assertThat (comparisonResult) .isPositive ();

ההשוואה הבסיסית מושגת באמצעות בהשוואה ל() השיטה, כך שהוויכוחים הועברו ל לְהַשְׁווֹת() השיטות חייבות להיות פרימיטיביות או ניתן להשוותס.

9. מסקנה

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

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


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