2011-09-25 27 views
10

Tôi đã phát hiện thấy một hành vi lạ đối với các nhóm có thể thay đổi mà tôi không thể hiểu:Scala: Chứa các bộ có thể thay đổi và không thay đổi

Tôi có một đối tượng mà tôi muốn thêm vào bộ. Phương thức equals cho lớp được ghi đè. Khi tôi thêm hai đối tượng khác nhau vào tập hợp, tạo ra cùng một đầu ra cho phương thức equals, tôi nhận được một hành vi khác nhau giữa các bộ có thể thay đổi và không thay đổi được cho phương thức chứa.

Dưới đây là đoạn mã:

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"))) 

này tạo ra như đầu ra:

Set(test) 
false 
Set(test) 
true 

Theo tôi cả hai cuộc gọi của chứa nên sản xuất cùng một sản lượng (true).

Ai đó có thể giúp tôi hiểu sự khác biệt ở đây hoặc đây có phải là lỗi trong việc thực hiện thiết lập không thay đổi được không? Nhân tiện, tôi sử dụng scala 2.8.1.final

Cảm ơn.

Trả lời

23

Quy tắc 1 khi triển khai bằng(): Triển khai hashCode() cùng một lúc. Xem Overriding equals and hashCode in Java

Trong ví dụ đầu tiên, bạn đang tạo một tập hợp có thể thay đổi, được gọi là hashCode để thiết lập bảng băm.

Trong lần thứ hai, bạn đang sử dụng bộ không thay đổi với một mục nhập, do đó, Scala thực sự sử dụng phiên bản được tối ưu hóa của Tập được gọi là Set1. Set1.contains() chỉ so sánh một mục với phần tử được truyền bằng cách sử dụng equals() trực tiếp. Điều này có dạng:

/** 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) 
    } 
} 

Không có hashCode được gọi. Ngoài ra còn có Set2, Set3 và Set4.

Vì vậy, nếu chúng ta thay đổi mã của bạn là:

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"))) 

thêm một hashCode và println trong bằng, và đầu ra là:

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

đó giải thích tại sao mutable.contains () không hoạt động chính xác. Nó đang tìm kiếm đối tượng trong mục bảng băm sai, bằng() thậm chí không được gọi. Và, không ngạc nhiên, nó không tìm thấy nó.

Bạn có thể thực hiện hashCode sử dụng text.hashCode:

override def hashCode: Int = text.hashCode 
+0

Chính xác như Matthew nói :-) –

+0

Cảm ơn bạn, tôi đã không nhận ra, rằng có những trường hợp đặc biệt cho các bộ không thay đổi kích thước 1. – Stefan

7

Bạn cần phải ghi đè hashCode là tốt. hashCode là điều cần thiết để ghi đè khi bạn ghi đè equals.

Note cũng có những một vài điều mà không biên dịch, vì vậy tôi thay đổi nội dung hơn một chút:

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"))) 

tôi khuyên bạn nên đọc http://www.artima.com/pins1ed/object-equality.html cho rất nhiều những hiểu biết về làm bình đẳng đối tượng. Đó là mở mắt.

+0

Xin lỗi vì lỗi cú pháp. Lần tới tôi sẽ nhìn hai lần. – Stefan

+0

Mã bạn đã cung cấp ở trên hoạt động tốt. Lý do cho hành vi "lạ" là như Matthew nói. – Stefan

Các vấn đề liên quan