JavaRush /จาวาบล็อก /Random-TH /วิธีการเท่ากับ & hashCode: การฝึกปฏิบัติ

วิธีการเท่ากับ & hashCode: การฝึกปฏิบัติ

เผยแพร่ในกลุ่ม
สวัสดี! วันนี้เราจะพูดถึงสองวิธีที่สำคัญใน Java - equals()และhashCode(). นี่ไม่ใช่ครั้งแรกที่เราได้พบพวกเขา ในช่วงเริ่มต้นของหลักสูตร JavaRush มีการบรรยาย สั้นๆ เกี่ยวกับequals()- อ่านหากคุณลืมหรือไม่เคยเห็นมาก่อน วิธีการเท่ากับ &  hashCode: แบบฝึกหัดการใช้งาน - 1ในบทเรียนวันนี้ เราจะพูดถึงแนวคิดเหล่านี้โดยละเอียด เชื่อฉันสิ มีเรื่องให้พูดมากมาย! และก่อนที่เราจะไปยังสิ่งใหม่ๆ มารีเฟรชความทรงจำของเราเกี่ยวกับสิ่งที่เราได้กล่าวถึงไปแล้ว :) ดังที่คุณจำได้ การเปรียบเทียบตามปกติของวัตถุสองชิ้นโดยใช้ตัว ดำเนินการ “ ==” เป็นความคิดที่ไม่ดี เนื่องจาก “ ==” เปรียบเทียบการอ้างอิง นี่คือตัวอย่างของเราเกี่ยวกับรถยนต์จากการบรรยายครั้งล่าสุด:
public class Car {

   String model;
   int maxSpeed;

   public static void main(String[] args) {

       Car car1 = new Car();
       car1.model = "Ferrari";
       car1.maxSpeed = 300;

       Car car2 = new Car();
       car2.model = "Ferrari";
       car2.maxSpeed = 300;

       System.out.println(car1 == car2);
   }
}
เอาต์พุตคอนโซล:

false
ดูเหมือนว่าเราได้สร้างอ็อบเจ็กต์ที่เหมือนกันสองรายการของคลาสCar: ฟิลด์ทั้งหมดในเครื่องทั้งสองเหมือนกัน แต่ผลลัพธ์ของการเปรียบเทียบยังคงเป็นเท็จ เรารู้เหตุผลแล้ว: ลิงก์car1และcar2ชี้ไปยังที่อยู่ต่าง ๆ ในหน่วยความจำจึงไม่เท่ากัน เรายังต้องการเปรียบเทียบสองวัตถุ ไม่ใช่สองการอ้างอิง ทางออกที่ดีที่สุดสำหรับการเปรียบเทียบวัตถุคือequals().

วิธีการเท่ากับ ()

คุณอาจจำได้ว่าเราไม่ได้สร้างวิธีนี้ตั้งแต่เริ่มต้น แต่แทนที่มัน - หลังจากนั้น วิธีการก็equals()ถูกกำหนดไว้ในคลาส Objectอย่างไรก็ตาม ในรูปแบบปกตินั้นมีประโยชน์เพียงเล็กน้อย:
public boolean equals(Object obj) {
   return (this == obj);
}
นี่ คือ วิธีequals()การกำหนดในคลาส Objectการเปรียบเทียบลิงก์เดียวกัน ทำไมเขาถึงถูกสร้างมาแบบนี้? ผู้สร้างภาษาจะรู้ได้อย่างไรว่าวัตถุใดในโปรแกรมของคุณถือว่าเท่ากันและวัตถุใดไม่? :) นี่คือแนวคิดหลักของวิธีการequals()- ผู้สร้างคลาสเองกำหนดลักษณะที่จะตรวจสอบความเท่าเทียมกันของวัตถุในคลาสนี้ โดยการทำเช่นนี้ คุณจะแทนที่วิธีการequals()ในชั้นเรียนของคุณ หากคุณยังไม่เข้าใจความหมายของ "คุณกำหนดลักษณะเฉพาะของตัวเอง" มากนัก ลองดูตัวอย่างกัน นี่คือบุคคลคลาสเรียบง่าย - Man.
public class Man {

   private String noseSize;
   private String eyesColor;
   private String haircut;
   private boolean scars;
   private int dnaCode;

public Man(String noseSize, String eyesColor, String haircut, boolean scars, int dnaCode) {
   this.noseSize = noseSize;
   this.eyesColor = eyesColor;
   this.haircut = haircut;
   this.scars = scars;
   this.dnaCode = dnaCode;
}

   //getters, setters, etc.
}
สมมติว่าเรากำลังเขียนโปรแกรมที่ต้องพิจารณาว่าคนสองคนมีความสัมพันธ์กันโดยฝาแฝดหรือแค่แฝดกัน เรามีคุณลักษณะห้าประการ: ขนาดจมูก สีตา ทรงผม รอยแผลเป็น และผลการทดสอบทางชีววิทยาของ DNA (เพื่อความง่าย - ในรูปแบบของหมายเลขรหัส) คุณคิดว่าคุณลักษณะใดต่อไปนี้จะช่วยให้โปรแกรมของเราระบุญาติฝาแฝดได้ วิธีการเท่ากับ &  hashCode: แบบฝึกหัดการใช้งาน - 2แน่นอนว่ามีเพียงการทดสอบทางชีววิทยาเท่านั้นที่สามารถรับประกันได้ คนสองคนอาจมีสีตา ทรงผม จมูก และแม้แต่รอยแผลเป็นเหมือนกัน - มีคนมากมายในโลกนี้ และเป็นไปไม่ได้ที่จะหลีกเลี่ยงเรื่องบังเอิญ เราต้องการกลไกที่เชื่อถือได้: เฉพาะผลการตรวจ DNA เท่านั้นที่ช่วยให้เราสรุปผลได้อย่างแม่นยำ สิ่งนี้หมายความว่าอย่างไรสำหรับวิธีการของเราequals()? เราจำเป็นต้องกำหนดมันใหม่ในชั้นเรียนManโดยคำนึงถึงข้อกำหนดของโปรแกรมของเรา วิธีการจะต้องเปรียบเทียบเขตข้อมูลint dnaCodeของวัตถุสองชิ้น และหากเท่ากัน วัตถุก็จะเท่ากัน
@Override
public boolean equals(Object o) {
   Man man = (Man) o;
   return dnaCode == man.dnaCode;
}
มันง่ายขนาดนั้นเลยเหรอ? ไม่เชิง. เราพลาดบางสิ่งบางอย่าง ในกรณีนี้สำหรับวัตถุของเราเราได้กำหนดฟิลด์ "สำคัญ" เพียงช่องเดียวเท่านั้นซึ่งสร้างความเท่าเทียมกัน - dnaCode. ทีนี้ลองจินตนาการว่าเราจะไม่มี 1 แต่มี 50 ช่องที่ "สำคัญ" เช่นนั้น และถ้าทั้ง 50 ช่องของวัตถุทั้งสองเท่ากันแสดงว่าวัตถุนั้นเท่ากัน สิ่งนี้อาจเกิดขึ้นได้เช่นกัน ปัญหาหลักคือการคำนวณความเท่าเทียมกันของ 50 ฟิลด์เป็นกระบวนการที่ใช้เวลานานและสิ้นเปลืองทรัพยากร ทีนี้ ลองจินตนาการว่านอกเหนือจากคลาสแล้วManเรายังมีคลาสWomanที่มีฟิลด์เหมือนกับในMan. และถ้าโปรแกรมเมอร์คนอื่นใช้คลาสของคุณ เขาก็สามารถเขียนโปรแกรมของเขาได้อย่างง่ายดายดังนี้:
public static void main(String[] args) {

   Man man = new Man(........); //a bunch of parameters in the constructor

   Woman woman = new Woman(.........);//same bunch of parameters.

   System.out.println(man.equals(woman));
}
ในกรณีนี้ ไม่มีประโยชน์ในการตรวจสอบค่าฟิลด์: เราเห็นว่าเรากำลังดูออบเจ็กต์ที่มีคลาสที่แตกต่างกันสองคลาส และโดยหลักการแล้วพวกมันไม่สามารถเท่ากันได้! ซึ่งหมายความว่าเราจำเป็นต้องทำเครื่องหมายในเมธอดequals()ซึ่งเป็นการเปรียบเทียบออบเจ็กต์ของสองคลาสที่เหมือนกัน เป็นเรื่องดีที่เราคิดเรื่องนี้!
@Override
public boolean equals(Object o) {
   if (getClass() != o.getClass()) return false;
   Man man = (Man) o;
   return dnaCode == man.dnaCode;
}
แต่บางทีเราลืมอย่างอื่นไปหรือเปล่า? อืม... อย่างน้อยที่สุดเราควรตรวจสอบว่าเราไม่ได้เปรียบเทียบวัตถุกับตัวมันเอง! หากการอ้างอิง A และ B ชี้ไปยังที่อยู่เดียวกันในหน่วยความจำ แสดงว่าทั้งสองเป็นวัตถุเดียวกัน และเราไม่จำเป็นต้องเสียเวลาในการเปรียบเทียบ 50 ฟิลด์
@Override
public boolean equals(Object o) {
   if (this == o) return true;
   if (getClass() != o.getClass()) return false;
   Man man = (Man) o;
   return dnaCode == man.dnaCode;
}
นอกจากนี้ การเพิ่มการตรวจสอบจะไม่เสียหายสำหรับnull: ไม่มีวัตถุใดสามารถเท่ากับ ได้nullซึ่งในกรณีนี้จะไม่มีประโยชน์ในการตรวจสอบเพิ่มเติม เมื่อคำนึงถึงทั้งหมดนี้ วิธี equals()การเรียน ของเรา Manจะมีลักษณะดังนี้:
@Override
public boolean equals(Object o) {
   if (this == o) return true;
   if (o == null || getClass() != o.getClass()) return false;
   Man man = (Man) o;
   return dnaCode == man.dnaCode;
}
เราดำเนินการตรวจสอบเบื้องต้นทั้งหมดที่กล่าวมาข้างต้น หากปรากฎว่า:
  • เราเปรียบเทียบวัตถุสองชิ้นในคลาสเดียวกัน
  • นี่ไม่ใช่วัตถุเดียวกัน
  • เราไม่ได้เปรียบเทียบวัตถุของเราด้วยnull
...จากนั้นเรามาดูการเปรียบเทียบลักษณะสำคัญกัน ในกรณีของเรา สนามdnaCodeของวัตถุสองชิ้น เมื่อแทนที่วิธีการequals()ต้องแน่ใจว่าได้ปฏิบัติตามข้อกำหนดเหล่านี้:
  1. สะท้อนแสง

    วัตถุใด ๆ จะต้องเป็นequals()ของตัวมันเอง
    เราได้คำนึงถึงข้อกำหนดนี้แล้ว วิธีการของเราระบุว่า:

    if (this == o) return true;

  2. สมมาตร.

    ถ้าa.equals(b) == trueอย่างนั้นb.equals(a)ก็ควรจะกลับtrueมา
    วิธีการของเราก็เป็นไปตามข้อกำหนดนี้เช่นกัน

  3. การขนส่ง

    หากวัตถุสองชิ้นมีค่าเท่ากับวัตถุชิ้นที่สาม วัตถุทั้งสองจะต้องเท่ากัน
    ถ้าa.equals(b) == trueและa.equals(c) == trueเช็คb.equals(c)ควรคืนค่าเป็นจริงด้วย

  4. ความคงทน.

    ผลลัพธ์ของงานequals()ควรเปลี่ยนแปลงเฉพาะเมื่อฟิลด์ที่รวมอยู่ในนั้นเปลี่ยนแปลงเท่านั้น หากข้อมูลของวัตถุทั้งสองไม่มีการเปลี่ยนแปลง ผลลัพธ์ของการตรวจสอบequals()ควรจะเหมือนกันเสมอ

  5. ความไม่เท่าเทียมกับnull.

    สำหรับอ็อบเจ็กต์ใด ๆ เช็คa.equals(null)จะต้องคืนค่าเท็จ
    นี่ไม่ใช่แค่ชุด "คำแนะนำที่เป็นประโยชน์" บางส่วน แต่เป็นสัญญาวิธีการ ที่เข้มงวด ซึ่งกำหนดไว้ในเอกสารของ Oracle

วิธีการ hashCode()

ตอนนี้เรามาพูดถึงวิธีการhashCode()กัน เหตุใดจึงจำเป็น? เพื่อจุดประสงค์เดียวกันทุกประการ - การเปรียบเทียบวัตถุ แต่เรามีมันแล้วequals()! ทำไมต้องใช้วิธีอื่น? คำตอบนั้นง่ายมาก: เพื่อปรับปรุงประสิทธิภาพการผลิต ฟังก์ชันแฮชซึ่งแสดงโดยเมธอด ใน Java hashCode()จะส่งกลับค่าตัวเลขที่มีความยาวคงที่สำหรับอ็อบเจ็กต์ใดๆ ในกรณีของ Java วิธีการhashCode()ส่งคืนชนิดจำนวน 32 บิต intการเปรียบเทียบตัวเลขสองตัวด้วยกันจะเร็วกว่าการเปรียบเทียบวัตถุสองตัวโดยใช้เมธอด โดยequals()เฉพาะอย่างยิ่งหากใช้หลายช่อง หากโปรแกรมของเราจะเปรียบเทียบวัตถุ จะง่ายกว่ามากถ้าใช้รหัสแฮช และเฉพาะในกรณีที่วัตถุมีค่าเท่ากันhashCode()- ดำเนินการเปรียบเทียบequals()ต่อ อย่างไรก็ตาม นี่คือวิธีการทำงานของโครงสร้างข้อมูลแบบแฮช—ตัวอย่างเช่น โครงสร้างที่คุณรู้จักHashMap! วิธีการhashCode()เช่นเดียวกับequals()ถูกแทนที่โดยนักพัฒนาเอง และเช่นเดียวกับ for equals()วิธีการนี้hashCode()มีข้อกำหนดอย่างเป็นทางการที่ระบุไว้ในเอกสารของ Oracle:
  1. หากวัตถุสองชิ้นเท่ากัน (นั่นคือเมธอดequals()คืนค่าจริง) วัตถุเหล่านั้นจะต้องมีรหัสแฮชเดียวกัน

    ไม่เช่นนั้นวิธีการของเราก็จะไร้ความหมาย การตรวจสอบโดยhashCode()อย่างที่เรากล่าวไว้ควรมาก่อนเพื่อปรับปรุงประสิทธิภาพ หากรหัสแฮชแตกต่างกัน การตรวจสอบจะส่งกลับเท็จ แม้ว่าจริง ๆ แล้ววัตถุจะเท่ากันก็ตาม (ตามที่เรากำหนดไว้ใน method equals())

  2. หากhashCode()มีการเรียกเมธอดหลายครั้งบนออบเจ็กต์เดียวกัน ก็ควรส่งคืนหมายเลขเดียวกันในแต่ละครั้ง

  3. กฎข้อที่ 1 ไม่ทำงานในทางกลับกัน ออบเจ็กต์สองรายการที่แตกต่างกันสามารถมีรหัสแฮชเดียวกันได้

กฎข้อที่สามค่อนข้างสับสนเล็กน้อย เป็นไปได้ยังไง? คำอธิบายค่อนข้างง่าย วิธีการhashCode()ส่งintคืน intเป็นตัวเลข 32 บิต มีค่าจำนวนจำกัด - ตั้งแต่ -2,147,483,648 ถึง +2,147,483,647 กล่าวอีกนัยหนึ่ง มีตัวเลขมากกว่า 4 พันล้านรูปintแบบ ทีนี้ลองจินตนาการว่าคุณกำลังสร้างโปรแกรมเพื่อจัดเก็บข้อมูลเกี่ยวกับสิ่งมีชีวิตทั้งหมดบนโลก แต่ละคนจะมีวัตถุในชั้นเรียนของManตนเอง ผู้คนประมาณ 7.5 พันล้านคนอาศัยอยู่บนโลก กล่าวอีกนัยหนึ่ง ไม่ว่าManเราจะเขียนอัลกอริทึมเพื่อแปลงวัตถุเป็นตัวเลขได้ดีเพียงใด เราก็จะมีตัวเลขไม่เพียงพอ เรามีทางเลือกเพียง 4.5 พันล้านตัวเลือก และผู้คนอีกมากมาย ซึ่งหมายความว่าไม่ว่าเราจะพยายามแค่ไหน รหัสแฮชก็จะเหมือนกันสำหรับบางคน สถานการณ์นี้ (รหัสแฮชของวัตถุสองรายการที่ตรงกัน) เรียกว่าการชนกัน เป้าหมายประการหนึ่งของโปรแกรมเมอร์เมื่อแทนที่เมธอดhashCode()คือการลดจำนวนการชนที่อาจเกิดขึ้นให้มากที่สุด hashCode()วิธี การเรียนของเราจะ เป็นอย่างไร Manโดยคำนึงถึงกฎเหล่านี้ทั้งหมด แบบนี้:
@Override
public int hashCode() {
   return dnaCode;
}
น่าประหลาดใจ? :) โดยไม่คาดคิด แต่ถ้าคุณดูข้อกำหนดจะพบว่าเราปฏิบัติตามทุกอย่าง วัตถุที่เราequals()คืนค่าจริงจะเท่ากันhashCode()ใน หากวัตถุทั้งสองของเราManมีค่าเท่ากันequals(นั่นคือ พวกมันมีค่าเท่ากันdnaCode) วิธีการของเราจะส่งคืนตัวเลขที่เท่ากัน ลองดูตัวอย่างที่ซับซ้อนกว่านี้ สมมติว่าโปรแกรมของเราควรเลือกรถหรูสำหรับลูกค้านักสะสม การสะสมเป็นสิ่งที่ซับซ้อนและมีคุณสมบัติมากมาย รถยนต์จากปี 1963 อาจมีราคาสูงกว่ารถคันเดียวกันจากปี 1964 ถึง 100 เท่า รถสีแดงจากปี 1970 อาจมีราคาสูงกว่ารถสีน้ำเงินยี่ห้อเดียวกันในปีเดียวกันถึง 100 เท่า วิธีการเท่ากับ &  hashCode: แบบฝึกหัดการใช้งาน - 4ในกรณีแรก สำหรับคลาส เราละทิ้งฟิลด์ส่วนใหญ่ (เช่น คุณลักษณะของบุคคล) ว่าไม่มีนัยสำคัญ และใช้ เฉพาะManฟิลด์เพื่อการเปรียบเทียบเท่านั้น dnaCodeที่นี่เรากำลังทำงานกับพื้นที่ที่มีเอกลักษณ์เฉพาะตัว และต้องไม่มีรายละเอียดเล็กๆ น้อยๆ! นี่คือชั้นเรียนของเราLuxuryAuto:
public class LuxuryAuto {

   private String model;
   private int manufactureYear;
   private int dollarPrice;

   public LuxuryAuto(String model, int manufactureYear, int dollarPrice) {
       this.model = model;
       this.manufactureYear = manufactureYear;
       this.dollarPrice = dollarPrice;
   }

   //... getters, setters, etc.
}
ในที่นี้เมื่อเปรียบเทียบเราต้องคำนึงถึงทุกสาขาด้วย ความผิดพลาดใดๆ ก็ตามอาจทำให้ลูกค้าต้องเสียเงินหลายแสนดอลลาร์ ดังนั้นจึงเป็นการดีกว่าที่จะปลอดภัย:
@Override
public boolean equals(Object o) {
   if (this == o) return true;
   if (o == null || getClass() != o.getClass()) return false;

   LuxuryAuto that = (LuxuryAuto) o;

   if (manufactureYear != that.manufactureYear) return false;
   if (dollarPrice != that.dollarPrice) return false;
   return model.equals(that.model);
}
ในวิธีการของเราequals()เราไม่ลืมเกี่ยวกับเช็คทั้งหมดที่เราพูดถึงก่อนหน้านี้ แต่ตอนนี้เราเปรียบเทียบแต่ละช่องในสามช่องของวัตถุของเรา ในโปรแกรมนี้ความเสมอภาคจะต้องสมบูรณ์ในทุกสาขา แล้วไงล่ะhashCode?
@Override
public int hashCode() {
   int result = model == null ? 0 : model.hashCode();
   result = result + manufactureYear;
   result = result + dollarPrice;
   return result;
}
ฟิลด์modelในชั้นเรียนของเราคือสตริง สะดวก: Stringวิธีการ นี้ hashCode()ถูกแทนที่ในชั้นเรียนแล้ว เราคำนวณรหัสแฮชของฟิลด์modelและบวกผลรวมของฟิลด์ตัวเลขอีกสองฟิลด์ มีเคล็ดลับเล็กๆ น้อยๆ ใน Java ที่ใช้เพื่อลดจำนวนการชนกัน: เมื่อคำนวณโค้ดแฮช ให้คูณผลลัพธ์ระดับกลางด้วยจำนวนเฉพาะที่เป็นคี่ หมายเลขที่ใช้กันมากที่สุดคือ 29 หรือ 31 เราจะไม่ลงรายละเอียดคณิตศาสตร์ในขณะนี้ แต่สำหรับการอ้างอิงในอนาคต โปรดจำไว้ว่าการคูณผลลัพธ์ระดับกลางด้วยจำนวนคี่ที่มากพอจะช่วย "กระจาย" ผลลัพธ์ของแฮช ทำงานและจบลงด้วยวัตถุน้อยลงที่มีแฮชโค้ดเดียวกัน สำหรับวิธีการของเราhashCode()ใน LuxuryAuto จะมีลักษณะดังนี้:
@Override
public int hashCode() {
   int result = model == null ? 0 : model.hashCode();
   result = 31 * result + manufactureYear;
   result = 31 * result + dollarPrice;
   return result;
}
คุณสามารถอ่านเพิ่มเติมเกี่ยวกับความซับซ้อนทั้งหมดของกลไกนี้ได้ในโพสต์นี้บน StackOverflowรวมถึงในหนังสือ “ Effective Java ” ของ Joshua Bloch สุดท้ายนี้ มีอีกประเด็นสำคัญที่ควรกล่าวถึงอีกประการหนึ่ง equals()แต่ละครั้ง ที่แทนที่hashCode()เราได้เลือกฟิลด์บางฟิลด์ของออบเจ็กต์ ซึ่งถูกนำมาพิจารณาในวิธีการเหล่านี้ แต่เราสามารถคำนึงถึงสาขาต่าง ๆ ในequals()และ ได้ หรือไม่ hashCode()? ในทางเทคนิคเราทำได้ แต่นี่เป็นความคิดที่ไม่ดี และนี่คือเหตุผล:
@Override
public boolean equals(Object o) {
   if (this == o) return true;
   if (o == null || getClass() != o.getClass()) return false;

   LuxuryAuto that = (LuxuryAuto) o;

   if (manufactureYear != that.manufactureYear) return false;
   return dollarPrice == that.dollarPrice;
}

@Override
public int hashCode() {
   int result = model == null ? 0 : model.hashCode();
   result = 31 * result + manufactureYear;
   result = 31 * result + dollarPrice;
   return result;
}
ต่อไปนี้เป็นวิธีการของเราequals()สำหรับhashCode()คลาส LuxuryAuto วิธีการhashCode()ยังคงไม่เปลี่ยนแปลง และequals()เราลบฟิลด์ออก จากวิธี modelการ ตอนนี้แบบจำลองไม่ใช่คุณลักษณะสำหรับการเปรียบเทียบวัตถุสองชิ้นequals()ด้วย แต่ยังคงนำมาพิจารณาเมื่อคำนวณรหัสแฮช เราจะได้รับผลอะไร? มาสร้างรถสองคันแล้วลองดูสิ!
public class Main {

   public static void main(String[] args) {

       LuxuryAuto ferrariGTO = new LuxuryAuto("Ferrari 250 GTO", 1963, 70000000);
       LuxuryAuto ferrariSpider = new LuxuryAuto("Ferrari 335 S Spider Scaglietti", 1963, 70000000);

       System.out.println("Are these two objects equal to each other?");
       System.out.println(ferrariGTO.equals(ferrariSpider));

       System.out.println("What are their hash codes?");
       System.out.println(ferrariGTO.hashCode());
       System.out.println(ferrariSpider.hashCode());
   }
}

Эти два an object равны друг другу?
true
Какие у них хэш-codeы?
-1372326051
1668702472
ข้อผิดพลาด! โดยใช้ช่องที่แตกต่างกันสำหรับequals()และhashCode()เราละเมิดสัญญาที่จัดตั้งขึ้นสำหรับพวกเขา! วัตถุ สองชิ้นที่เท่ากันequals()จะต้องมีรหัสแฮชเดียวกัน เรามีความหมายที่แตกต่างกันสำหรับพวกเขา ข้อผิดพลาดดังกล่าวสามารถนำไปสู่ผลลัพธ์ที่เหลือเชื่อที่สุด โดยเฉพาะอย่างยิ่งเมื่อทำงานกับคอลเลกชันที่ใช้แฮช ดังนั้นเมื่อกำหนดใหม่equals()และhashCode()จะใช้ช่องเดียวกันให้ถูกต้อง การบรรยายค่อนข้างยาว แต่วันนี้คุณได้เรียนรู้สิ่งใหม่มากมาย! :) ได้เวลากลับไปแก้ไขปัญหาแล้ว!
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION