השוואה והשוואה בג'אווה

1. הקדמה

השוואות ב- Java די קלות - עד שלא.

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

2. הגדרת הדוגמא

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

נתחיל ביצירת פשוט שחקן מעמד:

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

לאחר מכן, בואו ליצור a PlayerSorter בכיתה ליצור את האוסף שלנו ולעשות ניסיון למיין אותו באמצעות Collections.sort:

ראשי סטטי ציבורי ריק (String [] args) {List footballTeam = ArrayList new (); שחקן שחקן 1 = שחקן חדש (59, "ג'ון", 20); נגן שחקן 2 = שחקן חדש (67, "רוג'ר", 22); שחקן שחקן 3 = שחקן חדש (45, "סטיבן", 24); footballTeam.add (שחקן 1); footballTeam.add (player2); footballTeam.add (שחקן 3); System.out.println ("לפני המיון:" + כדורגל צוות); Collections.sort (footballTeam); System.out.println ("לאחר המיון:" + כדורגל צוות); } 

כאן, כצפוי, התוצאה היא שגיאת זמן הידור:

מיון השיטה (רשימה) בסוג האוספים אינו ישים לטיעונים (ArrayList)

בואו נבין מה עשינו לא בסדר כאן.

3. ניתן להשוות

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

בהתאם, על מנת שנוכל למיין - עלינו להגדיר את שלנו שחקן האובייקט דומה להשוואה באמצעות יישום ה- ניתן להשוות מִמְשָׁק:

מחלקה ציבורית מנגנים מיישמים השוואה {// כמו שלפני @Override public int CompareTo (Player otherPlayer) {return Integer.compare (getRanking (), otherPlayer.getRanking ()); }} 

סדר המיון נקבע לפי ערך ההחזר של ה- בהשוואה ל()שיטה. ה Integer.compare (x, y) מחזירה -1 אם איקס זה פחות מ y, מחזיר 0 אם הם שווים, ומחזיר 1 אחרת.

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

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

לפני המיון: [ג'ון, רוג'ר, סטיבן] לאחר המיון: [סטיבן, ג'ון, רוג'ר]

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

4. משווה

ה משווה ממשק מגדיר א השווה (arg1, arg2) שיטה עם שני טיעונים המייצגים אובייקטים בהשוואה ופועלים באופן דומה ל- Comparable.compareTo () שיטה.

4.1. יוצר משווים

ליצור משווה, עלינו ליישם את משווה מִמְשָׁק.

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

מחלקה ציבורית PlayerRankingComparator מיישמת את Comparator {@Override public int השווה (Player FirstPlayer, Player secondPlayer) {return Integer.compare (firstPlayer.getRanking (), secondPlayer.getRanking ()); }}

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

מחלקה ציבורית PlayerAgeComparator מיישם את Comparator {@Override public int השווה (Player FirstPlayer, Player secondPlayer) {return Integer.compare (firstPlayer.getAge (), secondPlayer.getAge ()); }}

4.2. משווים בִּפְעוּלָה

כדי להדגים את הרעיון, בואו לשנות את שלנו PlayerSorter על ידי הצגת טיעון שני ל שיטת Collections.sort שהוא למעשה המקרה של משווה אנחנו רוצים להשתמש.

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

PlayerRankingComparator playerComparator = PlayerRankingComparator חדש (); Collections.sort (footballTeam, playerComparator); 

עכשיו, בואו ננהל את שלנו PlayerRankingSorter ל- ראה את התוצאה:

לפני המיון: [ג'ון, רוג'ר, סטיבן] לאחר מיון לפי דירוג: [סטיבן, ג'ון, רוג'ר]

אם אנחנו רוצים סדר מיון אחר, אנחנו רק צריכים לשנות את משווה אנו משתמשים:

PlayerAgeComparator playerComparator = PlayerAgeComparator חדש (); Collections.sort (footballTeam, playerComparator);

עכשיו, כשאנחנו מנהלים את שלנו PlayerAgeSorterאנו יכולים לראות סדר מיון אחר לפי גיל:

לפני המיון: [ג'ון, רוג'ר, סטיבן] לאחר מיון לפי גיל: [רוג'ר, ג'ון, סטיבן]

4.3. ג'אווה 8 משווים

Java 8 מספק דרכים חדשות להגדרה משווים באמצעות ביטויים למבדה ואת משווה () שיטת מפעל סטטית.

בואו נראה דוגמה מהירה כיצד להשתמש בביטוי למבדה ליצירת a משווה:

Comparator byRanking = (Player player1, Player player2) -> Integer.compare (player1.getRanking (), player2.getRanking ());

ה Comparator. השוואה method לוקח שיטה המחושבת את המאפיין שישמש להשוואת פריטים ומחזירה התאמה משווה למשל:

Comparator byRanking = Comparator .comparing (נגן :: getRanking); Comparator byAge = Comparator .comparing (נגן :: getAge);

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

5. משווה לעומת ניתן להשוות

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

ואז, עלינו לשאול את עצמנו מדוע להשתמש ב- משווה אם כבר יש לנו ניתן להשוות?

ישנן מספר סיבות לכך:

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

6. הימנעות מטריק החיסור

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

משווה השוואה = (p1, p2) -> p1.getRanking () - p2.getRanking ();

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

Player player1 = שחקן חדש (59, "John", Integer.MAX_VALUE); נגן שחקן 2 = שחקן חדש (67, "רוג'ר", -1); רשימת שחקנים = Arrays.asList (player1, player2); players.sort (משווה);

מאז -1 הוא הרבה פחות מה- מספר שלם.MAX_VALUE, "רוג'ר" צריך לבוא לפני ה"ג'ון "באוסף הממוין. עם זאת, עקב הצפה שלמה, ה- "מספר שלם. MAX_VALUE - (-1)" יהיה פחות מאפס. אז, בהתבסס על משווה / משווה חוזה, מספר שלם.MAX_VALUE הוא פחות מ -1, וזה ללא ספק שגוי.

מכאן שלמרות מה שציפינו, "ג'ון" מגיע לפני "רוג'ר" באוסף הממוין:

assertEquals ("John", players.get (0) .getName ()); assertEquals ("רוג'ר", players.get (1) .getName ());

7. מסקנה

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

להבנת נושאי מיון מתקדמים יותר, עיין במאמרים האחרים שלנו כגון Java 8 Comparator, Java 8 Comparison with Lambdas.

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