2011-09-25 3 views
10

मैं परिवर्तनशील सेट के लिए एक अजीब व्यवहार जो मैं नहीं समझ सकता की खोज की है में शामिल हैं। वर्ग के लिए बराबर विधि ओवरराइड है। जब मैं सेट में दो अलग-अलग ऑब्जेक्ट्स जोड़ता हूं, जो समान विधि के लिए समान आउटपुट उत्पन्न करता है, तो मुझे शामिल विधि के लिए परिवर्तनीय और अपरिवर्तनीय सेट के बीच एक अलग व्यवहार मिलता है।स्काला:</p> <p>मैं एक वस्तु जो मैं एक सेट में जोड़ने के लिए करना चाहते हैं: परिवर्तनशील और अपरिवर्तनीय सेट

class Test(text:String){ 
    override def equals(obj:Any) = obj match { 
    case t: Test => if (t.text == this.text) true else false 
    case _ => false 
    } 
    override def toString = text 
} 

val mutableSet:scala.collection.mutable.Set[Test] = scala.collection.mutable.Set.empty 
mutableSet += new Test("test") 
println(mutableSet) 
println(mutableSet.contains(new Test("test"))) 

val immutableSet:scala.collection.immutable.Set[Test] = scala.collection.immutable.Set.empty 
immutableSet += new Test("test") 
println(immutableSet) 
println(immutableSet.contains(new Test("test"))) 

यह आउटपुट के रूप में पैदा करता है:

Set(test) 
false 
Set(test) 
true 

मेरी राय में दोनों कॉल एक ही आउटपुट (सही) का उत्पादन करना चाहिए होता है

यहाँ कोड का टुकड़ा है।

क्या कोई मुझे यहां अंतर को समझने में मदद कर सकता है या क्या यह स्कैला अपरिवर्तनीय सेट कार्यान्वयन में एक बग है? वैसे, मैं स्कैला 2.8.1 का उपयोग करता हूं। फाइनल

धन्यवाद।

उत्तर

23

बराबर लागू करने पर नियम 1(): एक ही समय में हैशकोड() लागू करें। देखें Overriding equals and hashCode in Java

पहले उदाहरण में, आप एक म्यूटेबल सेट बना रहे हैं, जो हैशकोड को हैश टेबल सेट करने के लिए कॉल करता है।

दूसरे में, आप एक प्रविष्टि के साथ एक अपरिवर्तनीय सेट का उपयोग कर रहे हैं, इसलिए स्कैला वास्तव में सेट के एक अनुकूलित संस्करण का उपयोग करता है जिसे सेट 1 कहा जाता है। Set1.contains() सीधे बराबर() का उपयोग करके पास तत्व के साथ एक प्रविष्टि की तुलना करता है। ऐसा लगता है:

/** An optimized representation for immutable sets of size 1 */ 
@SerialVersionUID(1233385750652442003L) 
class Set1[A] private[collection] (elem1: A) extends Set[A] with Serializable { 
    override def size: Int = 1 
    def contains(elem: A): Boolean = 
    elem == elem1 
    def + (elem: A): Set[A] = 
    if (contains(elem)) this 
    else new Set2(elem1, elem) 
    def - (elem: A): Set[A] = 
    if (elem == elem1) Set.empty 
    else this 
    def iterator: Iterator[A] = 
    Iterator(elem1) 
    override def foreach[U](f: A => U): Unit = { 
    f(elem1) 
    } 
} 

कोई हैशकोड नहीं कहा जाता है। एक सेट 2, सेट 3 और सेट 4 भी है।

तो अगर हम बदलने के अपने कोड होने के लिए:

class Test(val text:String){ 
    override def equals(obj:Any) = { 
    println("equals=" + obj) 
    obj match { 
    case t: Test => if (t.text == this.text) true else false 
    case _ => false 
    }} 

    override def hashCode(): Int = { 
    println("hashCode=" + super.hashCode()) 
    super.hashCode() 
    } 
    override def toString = text 
} 

println("mutable") 
val mutableSet:scala.collection.mutable.Set[Test] = scala.collection.mutable.Set.empty 
mutableSet += new Test("test") 
println("mutableSet=" + mutableSet + " contains=" + mutableSet.contains(new Test("test"))) 

println("immutable") 
var immutableSet:scala.collection.immutable.Set[Test] = scala.collection.immutable.Set.empty 
immutableSet += new Test("test") 
println("immutableSet=" + immutableSet + " contains=" + immutableSet.contains(new Test("test"))) 

एक hashCode और बराबर में एक println जोड़ने, और उत्पादन है:

mutable 
hashCode=30936685 
hashCode=26956691 
mutableSet=Set(test) contains=false 
immutable 
equals=test 
immutableSet=Set(test) contains=true 

यही वजह है कि mutable.contains बताते हैं () सही ढंग से काम नहीं कर रहा है। यह गलत हैश टेबल प्रविष्टि में ऑब्जेक्ट को देख रहा है, बराबर() को भी कॉल नहीं किया जाता है। और, आश्चर्यजनक रूप से, यह नहीं मिला है।

आप text.hashCode का उपयोग कर hashCode लागू कर सकते हैं:

override def hashCode: Int = text.hashCode 
+0

बिल्कुल मैथ्यू कहता है :-) –

+0

धन्यवाद, मैंने यह नहीं पहचाना है कि आकार 1 के अपरिवर्तनीय सेट के लिए विशेष मामले हैं। – Stefan

7

साथ ही आप hashCode ओवरराइड करने के लिए की जरूरत है। hashCode ओवरराइड करने के लिए आवश्यक है जब आप equals ओवरराइड करते हैं।

नोट वहाँ भी कुछ चीजें है कि संकलन नहीं किया था, इसलिए मैं थोड़ा और संपादित:

class Test(val text:String){ // added val 
    override def equals(obj:Any) = obj match { 
    case t: Test => if (t.text == this.text) true else false 
    case _ => false 
    } 
    override def toString = text 
    override def hashCode = text.hashCode 
} 

val mutableSet:scala.collection.mutable.Set[Test] = scala.collection.mutable.Set.empty 
mutableSet += new Test("test") 
println(mutableSet) 
println(mutableSet.contains(new Test("test"))) 

val immutableSet:scala.collection.immutable.Set[Test] = scala.collection.immutable.Set.empty 
val immutableSet2 = immutableSet + new Test("test") // reassignment to val 
println(immutableSet2) 
println(immutableSet2.contains(new Test("test"))) 

मैं वस्तु समानता कर रही पर एक बहुत अधिक अंतर्दृष्टि के लिए http://www.artima.com/pins1ed/object-equality.html पढ़ने की सलाह देते। यह आंख खोलना है।

+0

वाक्यविन्यास त्रुटियों के लिए खेद है। अगली बार मैं दो बार देखता हूं। – Stefan

+0

आपके द्वारा प्रदान किया गया कोड ठीक काम करता है। "अजीब" व्यवहार का कारण मैथ्यू कहता है। – Stefan

संबंधित मुद्दे