สวัสดี! วันนี้เราจะพูดถึงการเปรียบเทียบวัตถุ อืม... แต่ดูเหมือนเราจะคุยกันเรื่องนี้มากกว่าหนึ่งครั้งแล้วเหรอ? : / เรารู้ว่า
แต่การเปรียบเทียบสิ่งของระหว่างกันอาจมีเป้าหมายที่แตกต่างกันโดยสิ้นเชิง! สิ่งที่ชัดเจนที่สุดคือการเรียงลำดับ ฉันคิดว่าถ้าคุณถูกบอกให้เรียงลำดับรายการ
ควรใช้ในกรณีใดบ้าง
ตัวอย่างเช่น เราต้องการการเรียงลำดับดังกล่าวใน 15% ของกรณี เห็นได้ชัดว่านี่ไม่เพียงพอที่จะตั้งค่าการคัดแยกตามธรรมชาติ
==
ตัวดำเนินการ “ ” ทำงานอย่างไร เช่นเดียวกับequals()
and วิธีการ hashCode()
การเปรียบเทียบไม่ได้เกี่ยวกับเรื่องนั้นจริงๆ ก่อนหน้านี้เราหมายถึง "การทดสอบวัตถุเพื่อความเท่าเทียมกัน" มากกว่า 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
นี้ และโดยธรรมชาติแล้ว Java ก็ไม่รู้เรื่องนี้เช่นกัน! เมื่อเราพยายามส่งCollections.sort()
รายการวัตถุ ไปยัง method 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);
}
}
และจริงๆ แล้ว ภาษารู้ได้อย่างไรว่าจะจัดเรียงวัตถุที่คุณเขียนได้อย่างไร? ขึ้นอยู่กับเป้าหมายของโปรแกรมของคุณ เราต้องสอน Java ให้เปรียบเทียบวัตถุเหล่านี้ และเปรียบเทียบวิธีที่เราต้องการ เพื่อจุดประสงค์นี้ Java จึงมีเครื่องมือพิเศษ - อินเทอร์เฟซ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}]
รถถูกจัดเรียงตามที่ควรจะเป็น! :) 
Comparable
? วิธีการเปรียบเทียบ ที่นำมาใช้Comparable
เรียกว่า "การเรียงลำดับตามธรรมชาติ" เนื่องจากในวิธีการที่compareTo()
คุณอธิบายวิธีการเปรียบเทียบที่พบบ่อยที่สุดที่จะใช้กับอ็อบเจ็กต์ของคลาสนี้ในโปรแกรมของคุณ Natural Ordering มีอยู่แล้วใน Java ตัวอย่างเช่น Java รู้ว่าสตริงมักเรียงลำดับตามตัวอักษร และตัวเลขมักเรียงลำดับตามค่าจากน้อยไปมาก ดังนั้น หากคุณเรียกใช้เมธอดในรายการตัวเลขหรือสตริงsort()
ก็จะถูกจัดเรียง หากในโปรแกรมของเรา รถยนต์ส่วนใหญ่จะถูกเปรียบเทียบและจัดเรียงตามปีที่ผลิต ก็คุ้มค่าที่จะกำหนดการเรียงลำดับตามธรรมชาติโดยใช้อินเทอร์เฟซComparable<Car>
และวิธีcompareTo()
การ แต่ถ้านั่นยังไม่เพียงพอสำหรับเราล่ะ? ลองจินตนาการว่าโปรแกรมของเราไม่ง่ายนัก ในกรณีส่วนใหญ่ การคัดแยกรถยนต์ตามธรรมชาติ (เรากำหนดตามปีที่ผลิต) เหมาะกับเรา แต่บางครั้งในหมู่ลูกค้าของเราก็ชื่นชอบการขับรถเร็ว หากเรากำลังเตรียมแค็ตตาล็อกรถยนต์ให้เลือกจะต้องสั่งตามความเร็วสูงสุด 
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
แล้ว นั่นคือทั้งหมด! วันนี้คุณได้เรียนรู้กลไกที่สำคัญมากสองประการที่คุณมักจะใช้ในโครงการจริงในที่ทำงาน แต่อย่างที่คุณทราบ ทฤษฎีที่ไม่มีการฝึกฝนนั้นไม่มีความหมายเลย ดังนั้นจึงถึงเวลาที่จะรวบรวมความรู้ของคุณและแก้ไขปัญหาต่าง ๆ ! :)
GO TO FULL VERSION