ตัวเปรียบเทียบและเทียบเคียงใน Java

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