JavaRush /בלוג Java /Random-HE /משווה בג'אווה

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

פורסם בקבוצה
שלום! היום נדבר על השוואת חפצים. הממ... אבל נראה שכבר דיברנו על זה יותר מפעם אחת? :/ אנחנו יודעים איך ==פועל האופרטור " ", כמו גם את שיטות equals()ו hashCode(). השוואה היא לא באמת על זה. בעבר, התכוונו יותר ל"בדיקת אובייקטים לשוויון". משווה בג'אווה - 1אבל להשוואת אובייקטים זה עם זה עשויות להיות מטרות שונות לחלוטין! הדבר הברור ביותר הוא המיון. אני חושב שאם אומרים לך למיין רשימה של ArrayList<>מספרים או מחרוזות, אתה יכול להתמודד עם זה ללא בעיות:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       String name1 = "Masha";
       String name2 = "Sasha";
       String name3 = "Даша";

       List<String> names = new ArrayList<>();
       names.add(name1);
       names.add(name2);
       names.add(name3);

       Collections.sort(names);
       System.out.println(names);
   }
}
פלט מסוף:

[Даша, Маша, Саша]
זה נהדר אם אתה זוכר את הכיתה Collectionsואת השיטה שלה sort(). אני חושב שגם עם מספרים לא יהיו בעיות. הנה משימה קשה יותר עבורך:
public class Car {

   private int manufactureYear;
   private String model;
   private int maxSpeed;

   public Car(int manufactureYear, String model, int maxSpeed) {
       this.manufactureYear = manufactureYear;
       this.model = model;
       this.maxSpeed = maxSpeed;
   }

   //...геттеры, сеттеры, toString()

}

import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<Car> cars = new ArrayList<>();

       Car ferrari = new Car(1990, "Ferrari 360 Spider", 310);
       Car lambo = new Car(2012, "Lamborghini Gallardo", 290);
       Car bugatti = new Car(2010, "Bugatti Veyron", 350);

       cars.add(ferrari);
       cars.add(bugatti);
       cars.add(lambo);
   }
}
זה מאוד פשוט: מחלקה Carו-3 מהאובייקטים שלה. היו אדיבים למיין את המכוניות ברשימה! בטח תשאלו: "איך צריך למיין אותם?" לפי השם, לפי שנת ייצור, לפי מהירות מרבית? שאלה מצוינת. אנחנו לא יודעים כרגע איך למיין אובייקטים של המחלקה Car. ובאופן טבעי, גם ג'אווה לא יודעת את זה! כאשר אנו מנסים להעביר Collections.sort()רשימה של אובייקטים לשיטה Car, נקבל שגיאה:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<Car> cars = new ArrayList<>();

       Car ferrari = new Car(1990, "Ferrari 360 Spider", 310);
       Car lambo = new Car(20012, "Lamborghini Gallardo", 290);
       Car bugatti = new Car(2010, "Bugatti Veyron", 350);

       cars.add(ferrari);
       cars.add(bugatti);
       cars.add(lambo);

       //ошибка компилятора!
       Collections.sort(cars);
   }
}
ובאמת, איך השפה יודעת בדיוק איך למיין את החפצים שאתה כותב? זה תלוי במטרות התוכנית שלך. עלינו איכשהו ללמד את ג'אווה להשוות בין האובייקטים הללו. ולהשוות את הדרך שבה אנחנו צריכים את זה. למטרה זו, לג'אווה יש כלי מיוחד - הממשק Comparable. באנגלית זה מתורגם ל"השוואה". על מנת שהאובייקטים שלנו Carיושוו זה לזה וימיינו איכשהו, המחלקה חייבת ליישם את הממשק הזה וליישם את השיטה היחידה שלה: compareTo():
public class Car implements Comparable<Car> {

   private int manufactureYear;
   private String model;
   private int maxSpeed;

   public Car(int manufactureYear, String model, int maxSpeed) {
       this.manufactureYear = manufactureYear;
       this.model = model;
       this.maxSpeed = maxSpeed;
   }

   @Override
   public int compareTo(Car o) {
       return 0;
   }

   //...геттеры, сеттеры, toString()

}
שים לב:ציינו את הממשק Comparable<Car>, לא רק Comparable. זהו ממשק מודפס, כלומר הוא דורש מפרט של המחלקה הספציפית שאליה הוא משויך. באופן עקרוני, <Car>אתה יכול להסיר אותו מהממשק, אבל אז הוא משווה אובייקטים כברירת מחדל Object. במקום שיטה compareTo(Car o)בכיתה שלנו יהיה לנו:
@Override
   public int compareTo(Object o) {
       return 0;
   }
כמובן, הרבה יותר קל לנו לעבוד איתו Car. בתוך השיטה compareTo()אנו מיישמים את ההיגיון להשוואת מכונות. נניח שאנחנו צריכים למיין אותם לפי שנת ייצור. בטח שמתם לב שהשיטה compareTo()מחזירה את הערך int, לא boolean. אל תתנו לזה להפתיע אתכם. העובדה היא שהשוואת שני אובייקטים נותנת לנו 3 אפשרויות אפשריות:
  • а < b
  • a > b
  • a == b.
יש לו booleanרק 2 ערכים - נכון ושקר, וזה לא נוח להשוואת אובייקטים. הכל intהרבה יותר פשוט. אם ערך ההחזר > 0הוא , אז a > b. אם התוצאה compareTo < 0היא , אז а < b. ובכן, אם התוצאה היא == 0, אז שני האובייקטים שווים: a == b. ללמד את הכיתה שלנו למיין מכוניות לפי שנת ייצור קל כמו הפגזת אגסים:
@Override
public int compareTo(Car o) {
   return this.getManufactureYear() - o.getManufactureYear();
}
מה קורה פה? אנו לוקחים חפץ מכונית אחד ( this), שנת הייצור של מכונית זו, ומפחיתים ממנה את שנת הייצור של מכונית אחרת (זו שאיתה אנו משווים את החפץ). אם שנת הייצור של המכונית הראשונה גדולה יותר, השיטה תחזור int > 0. מה שאומר שמכונית this >היא מכונית о. אם, להיפך, שנת הייצור של המכונית השנייה ( о) גדולה יותר, אז השיטה תחזיר מספר שלילי, ולכן о > this. ובכן, אם הם שווים, השיטה תחזור 0. מנגנון פשוט כזה כבר מספיק כדי למיין אוספים של חפצים Car! אתה לא צריך לעשות שום דבר אחר. הנה אתה:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<Car> cars = new ArrayList<>();

       Car ferrari = new Car(1990, "Ferrari 360 Spider", 310);
       Car lambo = new Car(2012, "Lamborghini Gallardo", 290);
       Car bugatti = new Car(2010, "Bugatti Veyron", 350);

       cars.add(ferrari);
       cars.add(bugatti);
       cars.add(lambo);

       //тут раньше была ошибка
       Collections.sort(cars);
       System.out.println(cars);
   }
}
פלט מסוף:

[Car{manufactureYear=1990, model='Ferrari 360 Spider', maxSpeed=310}, 
Car{manufactureYear=2010, model='Bugatti Veyron', maxSpeed=350}, 
Car{manufactureYear=2012, model='Lamborghini Gallardo', maxSpeed=290}]
המכוניות ממוינות כמו שצריך! :) Comparator ב-Java - 2באילו מקרים יש להשתמש בו Comparable? שיטת ההשוואה המיושמת Comparableנקראת "סדר טבעי". הסיבה לכך היא שבשיטה compareTo()אתה מתאר את שיטת ההשוואה הנפוצה ביותר שתשמש עבור אובייקטים ממחלקה זו בתוכנית שלך. הזמנה טבעית כבר קיימת בג'אווה. לדוגמה, Java יודעת שמחרוזות ממוינות לרוב בסדר אלפביתי, ומספרים לרוב ממוינים לפי ערך עולה. לכן, אם תקרא לשיטה ברשימה של מספרים או מחרוזות sort(), הם ימוינו. אם בתוכנית שלנו מכוניות ברוב המקרים יושוו וימוינו לפי שנת ייצור, אז כדאי להגדיר עבורן מיון טבעי באמצעות הממשק Comparable<Car>והשיטה compareTo(). אבל מה אם זה לא מספיק לנו? בואו נדמיין שהתוכנית שלנו לא כל כך פשוטה. ברוב המקרים מתאים לנו המיון הטבעי של המכוניות (אנחנו קובעים לפי שנת ייצור). אבל לפעמים בקרב לקוחותינו יש חובבי נהיגה מהירה. אם אנחנו מכינים עבורם קטלוג מכוניות לבחירה, יש להזמין אותן לפי מהירות מרבית. משווה בג'אווה - 3למשל, אנחנו צריכים מיון כזה ב-15% מהמקרים. ברור שזה לא מספיק כדי להגדיר מיון טבעי לפי Carמהירות במקום שנת ייצור. אבל אנחנו לא יכולים להתעלם מ-15% מהלקוחות. מה אנחנו עושים? כאן בא לעזרתנו ממשק נוסף - Comparator. בדיוק כמו , Comparableזה מודפס. מה ההבדל? Comparableהופך את החפצים שלנו ל"ניתנים להשוואה" ויוצר עבורם את סדר המיון הטבעי ביותר שישמש ברוב המקרים. Comparator- זוהי מחלקה נפרדת של "משווה" (התרגום מעט מגושם, אבל מובן). אם אנחנו צריכים ליישם מיון מסוים, אנחנו לא צריכים להיכנס לכיתה Carולשנות את ההיגיון compareTo(). במקום זאת, נוכל ליצור מחלקה נפרדת להשוואה בתוכנית שלנו וללמד אותה לבצע את המיון שאנו צריכים!
import java.util.Comparator;

public class MaxSpeedCarComparator implements Comparator<Car> {

   @Override
   public int compare(Car o1, Car o2) {
       return o1.getMaxSpeed() - o2.getMaxSpeed();
   }
}
כפי שאתה יכול לראות, שלנו Comparatorהוא די פשוט. יש רק שיטה אחת compare()- זוהי שיטת ממשק Comparator, אותה יש ליישם. זה לוקח שני עצמים כקלט Carומשווה את המהירות המרבית שלהם בדרך הרגילה (בחיסור). כאילו compareTo(), זה מחזיר את המספר int, עקרון ההשוואה זהה. איך נוכל להשתמש בזה? פשוט מאוד:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<Car> cars = new ArrayList<>();

       Car ferrari = new Car(1990, "Ferrari 360 Spider", 310);
       Car lambo = new Car(2012, "Lamborghini Gallardo", 290);
       Car bugatti = new Car(2010, "Bugatti Veyron", 350);

       cars.add(ferrari);
       cars.add(bugatti);
       cars.add(lambo);

       Comparator speedComparator = new MaxSpeedCarComparator();
       Collections.sort(cars, speedComparator);

       System.out.println(cars);
   }
}
פלט מסוף:

[Car{manufactureYear=2012, model='Lamborghini Gallardo', maxSpeed=290}, 
Car{manufactureYear=1990, model='Ferrari 360 Spider', maxSpeed=310}, 
Car{manufactureYear=2010, model='Bugatti Veyron', maxSpeed=350}]
אנחנו פשוט יוצרים אובייקט השוואה ומעבירים אותו לשיטה Collections.sort()יחד עם הרשימה שיש למיין. לאחר קבלת השוואת כקלט, השיטה sort()לא תשתמש במיון הטבעי המוגדר בשיטת compareTo()המחלקה Car. במקום זאת, הוא יחיל את אלגוריתם המיון מהמשוואה שהועבר אליו. אילו יתרונות זה נותן לנו? ראשית, תאימות לקוד הכתוב. יצרנו שיטת מיון חדשה וספציפית, תוך שמירה על הנוכחית, שתשמש ברוב המקרים. לא נגענו בכיתה בכלל Car. הוא Comparableנשאר כמו שהוא:
public class Car implements Comparable<Car> {

   private int manufactureYear;
   private String model;
   private int maxSpeed;

   public Car(int manufactureYear, String model, int maxSpeed) {
       this.manufactureYear = manufactureYear;
       this.model = model;
       this.maxSpeed = maxSpeed;
   }

   @Override
   public int compareTo(Car o) {
       return this.getManufactureYear() - o.getManufactureYear();
   }

   //...геттеры, сеттеры, toString()

}
שנית, גמישות. אנחנו יכולים להוסיף כמה סוגים שאנחנו רוצים. נניח, מיין מכוניות לפי צבע, מהירות, משקל, או כמה פעמים השתמשו במכונית בסרטי באטמן. זה מספיק רק כדי ליצור אחד נוסף Comparator. זה הכל! היום למדת שני מנגנונים חשובים מאוד שתשתמש בהם לעתים קרובות בפרויקטים אמיתיים בעבודה. אבל, כידוע, תיאוריה ללא תרגול היא כלום. לכן, הגיע הזמן לגבש את הידע שלך ולפתור מספר בעיות! :)
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION