คำตอบที่แท้จริง
ทำไมมี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
Object
s เป็นหลักไร้ประโยชน์โดยการซ้ำซ้อน (ทำให้a.equals(b)
เท่าเทียมกันไปa==b
และa.hashCode() == b.hashCode()
เท่ากับไปa==b
ยังเว้นแต่hashCode
และ / หรือequals
ถูกแทนที่หรือคุณ GC หลายร้อยหลายพันObject
s ในช่วงวงจรชีวิตของแอพลิเคชันของคุณ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
วิธีดูเหมือนสมเหตุสมผลจากระยะไกลโดยปฏิบัติที่ดีที่สุดของวันนี้