1. บทนำ
การเปรียบเทียบใน Java นั้นค่อนข้างง่าย - จนกว่าจะไม่เป็นเช่นนั้น
เมื่อทำงานกับประเภทที่กำหนดเองหรือพยายามเปรียบเทียบวัตถุที่ไม่มีการเปรียบเทียบโดยตรงเราจำเป็นต้องใช้กลยุทธ์การเปรียบเทียบ เราสามารถสร้างได้ง่ายๆ แต่ใช้ประโยชน์จากอินเทอร์เฟซComparatorหรือComparable
2. การตั้งค่าตัวอย่าง
มาดูตัวอย่างทีมฟุตบอล - ที่เราต้องการเรียงลำดับผู้เล่นตามอันดับของพวกเขา
เราจะเริ่มต้นด้วยการสร้างคลาสผู้เล่นง่ายๆ:
public class Player { private int ranking; private String name; private int age; // constructor, getters, setters }
จากนั้นให้สร้างคลาสPlayerSorterเพื่อสร้างคอลเล็กชันของเราและพยายามจัดเรียงโดยใช้Collections.sort :
public static void main(String[] args) { List footballTeam = new ArrayList(); Player player1 = new Player(59, "John", 20); Player player2 = new Player(67, "Roger", 22); Player player3 = new Player(45, "Steven", 24); footballTeam.add(player1); footballTeam.add(player2); footballTeam.add(player3); System.out.println("Before Sorting : " + footballTeam); Collections.sort(footballTeam); System.out.println("After Sorting : " + footballTeam); }
ตามที่คาดไว้สิ่งนี้ส่งผลให้เกิดข้อผิดพลาดเวลาคอมไพล์:
The method sort(List) in the type Collections is not applicable for the arguments (ArrayList)
มาทำความเข้าใจกันว่าเราทำอะไรผิดที่นี่
3. เทียบเคียง
ตามชื่อที่แนะนำComparableเป็นอินเทอร์เฟซที่กำหนดกลยุทธ์ในการเปรียบเทียบวัตถุกับวัตถุอื่นที่มีประเภทเดียวกัน สิ่งนี้เรียกว่า "การจัดลำดับตามธรรมชาติ" ของชั้นเรียน
ดังนั้นเพื่อให้สามารถจัดเรียงได้ - เราต้องกำหนดวัตถุPlayerของเราให้เทียบเคียงได้โดยใช้อินเทอร์เฟซที่เปรียบเทียบได้ :
public class Player implements Comparable { // same as before @Override public int compareTo(Player otherPlayer) { return Integer.compare(getRanking(), otherPlayer.getRanking()); } }
เรียงลำดับจะตัดสินใจโดยค่าตอบแทนของcompareTo ()วิธีการ Integer.compare (x, y)ผลตอบแทน -1 ถ้าxน้อยกว่าปีผลตอบแทน 0 ถ้าพวกเขากำลังเท่ากันและผลตอบแทนที่ 1 มิฉะนั้น
วิธีนี้จะส่งคืนตัวเลขที่ระบุว่าวัตถุที่กำลังเปรียบเทียบนั้นน้อยกว่าเท่ากับหรือมากกว่าวัตถุที่ส่งผ่านเป็นอาร์กิวเมนต์
สุดท้ายเมื่อเราเรียกใช้PlayerSorterของเราตอนนี้เราจะเห็นผู้เล่นของเราเรียงตามอันดับของพวกเขา:
Before Sorting : [John, Roger, Steven] After Sorting : [Steven, John, Roger]
ตอนนี้เรามีความเข้าใจอย่างชัดเจนเกี่ยวกับการสั่งซื้อตามธรรมชาติด้วยComparableแล้วเรามาดูกันว่าเราจะใช้การสั่งซื้อประเภทอื่นได้อย่างไรในลักษณะที่ยืดหยุ่นกว่าการใช้อินเทอร์เฟซโดยตรง
4. เครื่องเปรียบเทียบ
เปรียบเทียบอินเตอร์เฟซที่กำหนดเปรียบเทียบ (arg1, arg2)วิธีการที่มีสองข้อโต้แย้งที่เป็นตัวแทนเมื่อเทียบกับวัตถุและทำงานคล้ายกับComparable.compareTo ()วิธีการ
4.1. การสร้างตัวเปรียบเทียบ
ในการสร้างตัวเปรียบเทียบเราต้องใช้อินเทอร์เฟซตัวเปรียบเทียบ
ในตัวอย่างแรกของเราเราจะสร้างตัวเปรียบเทียบเพื่อใช้แอตทริบิวต์การจัดอันดับของผู้เล่นเพื่อจัดเรียงผู้เล่น:
public class PlayerRankingComparator implements Comparator { @Override public int compare(Player firstPlayer, Player secondPlayer) { return Integer.compare(firstPlayer.getRanking(), secondPlayer.getRanking()); } }
ในทำนองเดียวกันเราสามารถสร้างตัวเปรียบเทียบเพื่อใช้คุณลักษณะอายุของผู้เล่นเพื่อจัดเรียงผู้เล่น:
public class PlayerAgeComparator implements Comparator { @Override public int compare(Player firstPlayer, Player secondPlayer) { return Integer.compare(firstPlayer.getAge(), secondPlayer.getAge()); } }
4.2. ตัวเปรียบเทียบในการดำเนินการ
เพื่อแสดงแนวคิดให้แก้ไขPlayerSorterของเราโดยแนะนำอาร์กิวเมนต์ที่สองให้กับเมธอด Collections.sortซึ่งเป็นอินสแตนซ์ของตัวเปรียบเทียบที่เราต้องการใช้
ด้วยวิธีนี้เราสามารถลบล้างลำดับธรรมชาติ :
PlayerRankingComparator playerComparator = new PlayerRankingComparator(); Collections.sort(footballTeam, playerComparator);
ตอนนี้ให้เรียกใช้PlayerRankingSorterของเราเพื่อดูผลลัพธ์:
Before Sorting : [John, Roger, Steven] After Sorting by ranking : [Steven, John, Roger]
หากเราต้องการลำดับการเรียงลำดับที่แตกต่างกันเราจำเป็นต้องเปลี่ยนตัวเปรียบเทียบที่เราใช้เท่านั้น:
PlayerAgeComparator playerComparator = new PlayerAgeComparator(); Collections.sort(footballTeam, playerComparator);
ตอนนี้เมื่อเราเรียกใช้PlayerAgeSorterเราจะเห็นลำดับการจัดเรียงที่แตกต่างกันตามอายุ:
Before Sorting : [John, Roger, Steven] After Sorting by age : [Roger, John, Steven]
4.3. ตัวเปรียบเทียบ Java 8
Java 8 นำเสนอวิธีการใหม่ในการกำหนดตัวเปรียบเทียบโดยใช้นิพจน์แลมบ์ดาและวิธีการเปรียบเทียบ ()โรงงานแบบคงที่
มาดูตัวอย่างวิธีการใช้แลมบ์ดานิพจน์เพื่อสร้างตัวเปรียบเทียบ :
Comparator byRanking = (Player player1, Player player2) -> Integer.compare(player1.getRanking(), player2.getRanking());
Comparator.comparingวิธีการใช้เวลาวิธีการคำนวณทรัพย์สินที่จะนำมาใช้สำหรับการเปรียบเทียบรายการและผลตอบแทนการจับคู่เปรียบเทียบเช่น:
Comparator byRanking = Comparator .comparing(Player::getRanking); Comparator byAge = Comparator .comparing(Player::getAge);
คุณสามารถสำรวจฟังก์ชันการทำงานของ Java 8 ในเชิงลึกได้ในคู่มือการเปรียบเทียบ Java 8 ของเรา
5. เปรียบเทียบ VS เทียบเคียง
เทียบเคียงกับอินเตอร์เฟซที่เป็นทางเลือกที่ดีเมื่อใช้ในการสั่งซื้อสำหรับการกำหนดค่าเริ่มต้นหรือในคำอื่น ๆ ถ้ามันเป็นวิธีหลักของการเปรียบเทียบวัตถุ
จากนั้นเราต้องถามตัวเองว่าทำไมต้องใช้Comparatorถ้าเรามีComparableแล้ว?
มีสาเหตุหลายประการดังนี้
- บางครั้งเราไม่สามารถแก้ไขซอร์สโค้ดของคลาสที่มีอ็อบเจ็กต์ที่เราต้องการจัดเรียงได้ทำให้ไม่สามารถใช้การเปรียบเทียบได้
- การใช้ตัวเปรียบเทียบช่วยให้เราหลีกเลี่ยงการเพิ่มโค้ดเพิ่มเติมในคลาสโดเมนของเรา
- เราสามารถกำหนดกลยุทธ์การเปรียบเทียบที่แตกต่างกันซึ่งไม่สามารถทำได้เมื่อใช้การเปรียบเทียบ
6. หลีกเลี่ยงเคล็ดลับการลบ
ในบทช่วยสอนนี้เราใช้วิธีInteger.compare ()เพื่อเปรียบเทียบจำนวนเต็มสองจำนวน อาจมีคนแย้งว่าเราควรใช้ one-liner ที่ชาญฉลาดนี้แทน:
Comparator comparator = (p1, p2) -> p1.getRanking() - p2.getRanking();
แม้ว่าจะมีความกระชับมากกว่าเมื่อเทียบกับโซลูชันอื่น ๆ แต่ก็สามารถตกเป็นเหยื่อของจำนวนเต็มล้นใน Java :
Player player1 = new Player(59, "John", Integer.MAX_VALUE); Player player2 = new Player(67, "Roger", -1); List players = Arrays.asList(player1, player2); players.sort(comparator);
เนื่องจาก -1 น้อยกว่าInteger.MAX_VALUEมาก "Roger" ควรอยู่ก่อน "John" ในคอลเล็กชันที่เรียงลำดับ แต่เนื่องจากการล้นจำนวนเต็มที่“Integer.MAX_VALUE - (-1)”จะมีค่าน้อยกว่าศูนย์ ดังนั้นขึ้นอยู่กับการเปรียบเทียบ / เทียบเคียงสัญญาที่Integer.MAX_VALUEน้อยกว่า -1 ซึ่งเป็นที่ไม่ถูกต้องอย่างเห็นได้ชัด
ดังนั้นแม้ว่าเราจะคาดหวังอะไร "จอห์น" ก็มาก่อน "โรเจอร์" ในคอลเล็กชันที่เรียงลำดับ:
assertEquals("John", players.get(0).getName()); assertEquals("Roger", players.get(1).getName());
7. สรุป
ในการกวดวิชานี้เราสำรวจเทียบเคียงและเปรียบเทียบอินเตอร์เฟซและกล่าวถึงความแตกต่างระหว่างพวกเขา
หากต้องการทำความเข้าใจหัวข้อการเรียงลำดับขั้นสูงเพิ่มเติมโปรดดูบทความอื่น ๆ ของเราเช่น Java 8 Comparator, Java 8 Comparison กับ Lambdas
และตามปกติซอร์สโค้ดสามารถพบได้บน GitHub