คำตอบที่แท้จริง
ทำไมมีComparatorอินเตอร์เฟซ แต่ไม่มีHasherและEquator?
คืออ้างความอนุเคราะห์จาก Josh Bloch :
Java API ดั้งเดิมนั้นทำเสร็จเร็วมากภายใต้กำหนดเวลาที่ จำกัด เพื่อพบกับหน้าต่างตลาดปิด ทีม Java ดั้งเดิมทำงานได้ดีมาก แต่ API ทั้งหมดนั้นไม่ได้สมบูรณ์แบบ
ปัญหาอยู่ แต่เพียงผู้เดียวในประวัติศาสตร์ของ Java เช่นเดียวกับเรื่องอื่นที่คล้ายคลึงกันเช่นVS.clone()Cloneable
TL; DR
สำหรับเหตุผลทางประวัติศาสตร์เป็นหลัก; พฤติกรรมปัจจุบัน / สิ่งที่เป็นนามธรรมถูกนำมาใช้ใน JDK 1.0 และไม่ได้รับการแก้ไขในภายหลังเพราะมันแทบเป็นไปไม่ได้ที่จะทำเช่นนั้นกับการรักษาความเข้ากันได้ของรหัสย้อนหลัง
ก่อนอื่นมาสรุปข้อเท็จจริงของ Java ที่รู้จักกันดี:
- Java ตั้งแต่เริ่มต้นจนถึงปัจจุบันมีความเข้ากันได้กับระบบย้อนหลังอย่างภาคภูมิใจซึ่งต้องใช้ API ดั้งเดิมที่ยังคงรองรับในเวอร์ชันที่ใหม่กว่า
- ด้วยเหตุนี้ภาษาเกือบทุกภาษาที่ใช้กับ JDK 1.0 จะมีชีวิตอยู่จนถึงปัจจุบัน
Hashtable, .hashCode()และ.equals()ถูกนำมาใช้ใน JDK 1.0 ( Hashtable )
Comparable/ Comparatorเป็นที่รู้จักใน JDK 1.2 ( เทียบเคียง )
ตอนนี้มันเป็นดังนี้:
- มันเป็นไปไม่ได้จริงและหมดสติที่จะติดตั้งเพิ่มเติม
.hashCode()และ.equals()อินเตอร์เฟซที่แตกต่างกันในขณะที่ยังคงรักษาความเข้ากันได้หลังจากที่ผู้คนได้ตระหนักถึงมีแนวคิดที่ดีกว่าวางไว้ใน superobject เพราะเช่นทุกคนโปรแกรม Java 1.2 รู้ว่าทุกคนObjectมีพวกเขาและพวกเขามี จะอยู่ที่นั่นร่างกายเพื่อให้รหัสเรียบเรียง (JVM) เข้ากันได้ยัง - และการเพิ่มอินเตอร์เฟซที่ชัดเจนในทุกObjectระดับชั้นย่อยที่ดำเนินการจริงๆพวกเขาจะทำให้ระเบียบนี้เท่ากับ (sic!) ไปClonableหนึ่ง ( กล่าวถึง Bloch ทำไม Cloneable ครับนอกจากนี้ยังกล่าวถึงในเช่น EJ 2 และสถานที่อื่น ๆ อีกมากมายรวมถึง SO)
- พวกเขาทิ้งไว้ที่นั่นเพื่อคนรุ่นต่อไปที่จะมีแหล่งที่มาของ WTF อย่างต่อเนื่อง
ตอนนี้คุณอาจถาม "สิ่งที่Hashtableมีทั้งหมดนี้"?
คำตอบคือ: hashCode()/ equals()contractและทักษะการออกแบบภาษาที่ไม่ดีนักของนักพัฒนา Java core ในปี 1995/1996
อ้างอิงจากJava 1.0 Language Spec, ลงวันที่ 1996 - 4.3.2 The Class Object, p.41:
วิธีการequalsและhashCodeมีการประกาศเพื่อประโยชน์ของjava.util.Hashtableแฮชเทเบิลเช่น(§21.7) วิธีการที่กำหนดเท่ากับความคิดของความเท่าเทียมกันของวัตถุซึ่งขึ้นอยู่กับมูลค่าไม่อ้างอิงการเปรียบเทียบ
(โปรดสังเกตว่าข้อความที่แน่นอนนี้มีการเปลี่ยนแปลงในรุ่นที่ใหม่กว่าเพื่อพูดอ้างว่า: The method hashCode is very useful, together with the method equals, in hashtables such as java.util.HashMap.ทำให้เป็นไปไม่ได้ที่จะทำให้การเชื่อมต่อโดยตรงHashtable- hashCode- equalsการเชื่อมต่อโดยไม่ต้องอ่าน JLS ประวัติศาสตร์!)
ทีม Java ตัดสินใจว่าพวกเขาต้องการคอลเลกชันสไตล์พจนานุกรมที่ดีและพวกเขาสร้างHashtable(ความคิดที่ดีจนถึงตอนนี้) แต่พวกเขาต้องการให้โปรแกรมเมอร์สามารถใช้มันด้วยโค้ด / การเรียนรู้น้อยที่สุดเท่าที่จะทำได้ (อุ๊ปส์! และเนื่องจากยังไม่มีข้อมูลทั่วไป [มันคือ JDK 1.0 หลังจากทั้งหมด] นั่นหมายความว่าทุกสิ่งที่ Objectใส่เข้าไปHashtableจะต้องใช้ส่วนต่อประสานอย่างชัดเจน (และส่วนต่อประสานก็ยังอยู่ในช่วงเริ่มต้น ... Comparableยังไม่ได้!) ทำให้นี้เป็นอุปสรรคที่จะใช้สำหรับจำนวนมาก - หรือObjectจะต้องปริยายใช้วิธีการบางอย่างคร่ำเครียด
เห็นได้ชัดว่าพวกเขาใช้โซลูชัน 2 ด้วยเหตุผลที่กล่าวไว้ข้างต้น ใช่ตอนนี้เรารู้ว่าพวกเขาผิด ... มันง่ายที่จะฉลาดในการเข้าใจถึงปัญหาหลังเหตุการณ์ ซิกซี้
ตอนนี้hashCode() ต้องการให้ทุกวัตถุที่มีมันต้องมีequals()วิธีการที่แตกต่างกัน - ดังนั้นจึงค่อนข้างชัดเจนว่าequals()จะต้องใส่Objectเช่นกัน
ตั้งแต่เริ่มต้นการใช้งานวิธีการเหล่านั้นในที่ถูกต้องaและb Objects เป็นหลักไร้ประโยชน์โดยการซ้ำซ้อน (ทำให้a.equals(b) เท่าเทียมกันไปa==bและa.hashCode() == b.hashCode() เท่ากับไปa==bยังเว้นแต่hashCodeและ / หรือequalsถูกแทนที่หรือคุณ GC หลายร้อยหลายพันObjects ในช่วงวงจรชีวิตของแอพลิเคชันของคุณ1 ) มันปลอดภัยที่จะบอกว่าพวกเขามีให้เป็นมาตรการสำรองและเพื่อความสะดวกในการใช้งาน นี่คือวิธีที่เราได้รับความจริงที่รู้จักกันดีซึ่งจะแทนที่ทั้งสอง.equals()& .hashCode()ถ้าคุณตั้งใจจะเปรียบเทียบวัตถุจริง ๆ หรือเก็บไว้แฮช. การเอาชนะเพียงคนเดียวโดยที่ไม่มีคนอื่นเป็นวิธีที่ดีในการไขรหัสของคุณ (โดยการเปรียบเทียบผลลัพธ์ที่ชั่วร้ายหรือค่าการชนกันของถังสูงอย่างไม่น่าเชื่อ) - การเอาหัวไปรอบ ๆ มันเป็นแหล่งที่มาของความสับสน สำหรับตัวคุณเอง) และสร้างความรำคาญให้กับคนที่มีประสบการณ์มากขึ้น
นอกจากนี้โปรดทราบว่าถึงแม้ว่า C # จะเกี่ยวข้องกับค่าเท่ากับ & hashcode ในทางที่ดีขึ้นเล็กน้อยEric Lippert ระบุว่าพวกเขาทำผิดพลาดเกือบเหมือนกันกับ C # ที่ Sun ทำกับ Java เมื่อหลายปีก่อนที่ C # จะเริ่มต้น :
แต่ทำไมมันเป็นกรณีที่วัตถุทุกชิ้นควรจะสามารถแฮชตัวเองเพื่อแทรกเข้าไปในตารางแฮช? ดูเหมือนว่าเป็นเรื่องแปลกที่ต้องให้ทุกวัตถุสามารถทำได้ ฉันคิดว่าถ้าเราออกแบบระบบพิมพ์ใหม่ตั้งแต่เริ่มต้นวันนี้การแปลงแป้นพิมพ์อาจทำแตกต่างกันอาจมีIHashableส่วนต่อประสาน แต่เมื่อระบบประเภท CLR ได้รับการออกแบบไม่มีประเภททั่วไปดังนั้นตารางแฮชวัตถุประสงค์ทั่วไปจึงจำเป็นต้องเก็บวัตถุใด ๆ
1แน่นอนObject#hashCodeยังสามารถชนกันได้ แต่ต้องใช้ความพยายามเล็กน้อยในการดูที่: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6809470และเชื่อมโยงรายงานข้อผิดพลาดเพื่อดูรายละเอียด; /programming/1381060/hashcode-uniqueness/1381114#1381114ครอบคลุมหัวข้อนี้ในเชิงลึกมากขึ้น
Personการใช้งานที่คาดหวังequalsและhashCodeพฤติกรรมHashMap<PersonWrapper, V>แล้วคุณต้องการมี นี่คือตัวอย่างหนึ่งที่วิธีการบริสุทธิ์แบบ OOP ไม่สง่างาม: ไม่ใช่การดำเนินการทุกอย่างบนวัตถุที่ทำให้รู้สึกว่าเป็นวิธีการของวัตถุนั้น ทั้งของ JavaObjectประเภทคือการรวมกันของความรับผิดชอบที่แตกต่างกัน - เพียงgetClass,finalizeและtoStringวิธีดูเหมือนสมเหตุสมผลจากระยะไกลโดยปฏิบัติที่ดีที่สุดของวันนี้