2011-11-16 36 views
0

使用下面的代碼產生了以下內容:斯卡拉List.diff .intersect和.union工作不正常

List(a, b) 
List() 
List(a, b, b, c) 

我想是當然這樣的結果:

List(a) 
List(b) 
List(a, b, b, c) 

我認爲這是因爲diff union和intersect相對於「==」工作。 問題是,「==」 - 運算符是最終的,不能在類「測試」中重寫。

我怎麼能達到我期望的結果?

這裏談到的使用代碼:

package scalatest; 

public class JStringHolder { 
String s = null; 

public JStringHolder(String newString){ 
    s = newString; 
} 

@Override 
public String toString() { 
    return s; 
} 

@Override 
public boolean equals(Object obj) { 
    System.out.println("SHEQ " + this.s + " AND " + ((JStringHolder)obj).s + " " + this.s.equals(((JStringHolder)obj).s)); 
    return this.s.equals(((JStringHolder)obj).s); 
} 
} 

和Scala代碼被執行:

package scalatest 

object ListTest { 

    trait AbstractTest 
    case class Test(stringHolder: scalatest.JStringHolder) extends AbstractTest { 
    override def toString = stringHolder.toString() 
    override def equals(ot: Any) : Boolean = { 
     return stringHolder.equals(ot.asInstanceOf[Test].stringHolder) 
    } 

    } 

    def main(args : Array[String]) : Unit = { 
    val l1 = List(Test(new JStringHolder("a")), Test(new JStringHolder("b"))) 
    val l2 = List(Test(new JStringHolder("b")), Test(new JStringHolder("c"))) 
    println (l1.diff(l2)) 
    println (l1.intersect(l2)) 
    println (l1.union(l2)) 
    } 
} 

回答

3

的問題是,你應該重寫hashCode方法的方式,即兩個實例顯示相同的hashCode,當你認爲它們是相同的。只有hashCode相同時,纔會調用equals方法,否則兩個實例不相同(至少在List.diff和intersect的意義上)。

使用下面的代碼將實現的結果我想:

List(a) 
List(b) 
List(a, b, b, c) 

這裏來所使用的代碼:

package scalatest; 

public class JStringHolder { 
String s = null; 

public JStringHolder(String newString){ 
    s = newString; 
} 

@Override 
public String toString() { 
    return s; 
} 

@Override 
public boolean equals(Object obj) { 
    return this.s.equals(((JStringHolder)obj).s); 
} 
@Override 
public int hashCode() { 
    return s.hashCode(); 
} 
} 

和Scala代碼被執行:

package scalatest 

object ListTest { 

    trait AbstractTest 
    case class Test(stringHolder: scalatest.JStringHolder) extends AbstractTest { 
    override def toString = stringHolder.toString() 
    override def equals(ot: Any) : Boolean = { 
     return stringHolder.equals(ot.asInstanceOf[Test].stringHolder) 
    } 
    override def hashCode() : Int = { 
     stringHolder.hashCode() 
    }  
    } 

    def main(args : Array[String]) : Unit = { 
    val l1 = List(Test(new JStringHolder("a")), Test(new JStringHolder("b"))) 
    val l2 = List(Test(new JStringHolder("b")), Test(new JStringHolder("c"))) 
    println (l1.diff(l2)) 
    println (l1.intersect(l2)) 
    println (l1.union(l2)) 
    } 
} 
0

你沒有太多的選擇超出了合理equals方法上的東西封裝這些值。

+0

整個問題是你需要重寫'hashCode'方法。這真的很愚蠢,但這是它目前的工作方式。希望scala更新'diff'實現,以便它基於'equals'而不是'hashCode' –

+0

@GeorgePligor當您更改'equals'時,通常需要重寫'hashCode' - 這不是可選的,它是'hashCode' API每個類都從'Object'繼承,它在'hashCode'和'equals' JavaDoc中都有描述。如果你不這樣做,那麼你的代碼就會被打破。 –

+0

你在說什麼可能是正確的,但我認爲這很愚蠢。我永遠不會像那樣設計它。對你來說沒有任何意義,但對那些隱含地創造你需要學習的限制的人來說,因爲編譯器永遠不會告訴你。 –

1

我不太確定:如果執行diff,可能是bug?使用已棄用的--而不是diff實際上適用於我。 Set而不是List也適用。

我沒有答案(只使用集作爲一種解決方法的建議),但我在這裏將我自己的測試,在情況下,他們可以幫助別人一個答案回來:

// re-implementing JStringHolder in scala 
// to show that this is not java-specific 
class JStringHolder(val st: String) { 
    override def equals(that: Any): Boolean = that match { 
     case t: JStringHolder => this.st == t.st 
     case _ => false 
    } 
} 
case class Test(stringHolder: JStringHolder) { 
    override def equals(that: Any) : Boolean = that match { 
     case t: Test => this.stringHolder == t.stringHolder 
     case _ => false 
    } 
} 

現在在REPL(斯卡拉2.9.0.1):

scala> List(Test(new JStringHolder("a")), Test(new JStringHolder("b"))) 
res0: List[Test] = List(a, b) 

scala> List(Test(new JStringHolder("c")), Test(new JStringHolder("b"))) 
res1: List[Test] = List(c, b) 

scala> res0 diff res1 
res2: List[Test] = List(a, b) 

- 已被棄用,但工程

scala> res0 -- res1 
<console>:13: warning: method -- in class List is deprecated: use `list1 filterN 
ot (list2 contains)` instead 
     res0 -- res1 
      ^
res3: List[Test] = List(a) 

幹什麼與filterNot摹吧,就像棄用警告提示,工程太

scala> res0 filterNot (res1 contains) 
res4: List[Test] = List(a) 

而且,表明一切正常,與Set

scala> Set(Test(new JStringHolder("a")), Test(new JStringHolder("b"))) 
res0: scala.collection.immutable.Set[Test] = Set(a, b) 

scala> Set(Test(new JStringHolder("b")), Test(new JStringHolder("c"))) 
res1: scala.collection.immutable.Set[Test] = Set(b, c) 

scala> res0 diff res1 
res2: scala.collection.immutable.Set[Test] = Set(a) 

scala> res0 union res1 
res3: scala.collection.immutable.Set[Test] = Set(a, b, c) 

scala> res0 intersect res1 
res4: scala.collection.immutable.Set[Test] = Set(b) 
+0

經過很多嘗試,我認爲它是hashCode方法。如果你正確覆蓋它。上面的代碼將工作得很好。謝謝大家的答案!必須是在後臺完成所有工作的哈希表。 –

+0

@John:你應該把你的正確的代碼放在答案中,並將其標記爲正確的(對於未來的其他人)。我仍然覺得有什麼奇怪的事情發生,因爲JStringHolder和Test的平等似乎有效('new JStringHolder(「a」)== new JStringHolder(「a」)'是true,'Test(new JStringHolder(「a」 ))==測試(新的JStringHolder(「a」))'是真的......) –

+0

我想插入它作爲答案,但我不能在提交問題後不到8小時內回答。稍後我會添加代碼......你說得對,如果你只用一個StringHolder來使用「==」,它就可以工作。問題是List的diff和intersect方法。他們似乎使用hashCode來區分實例... –

0
scala> object ListTest { 
    | 
    | trait AbstractTest 
    | case class Test (stringHolder: JStringHolder) extends AbstractTest { 
    |  override def toString = stringHolder.toString() 
    |  override def equals (ot: Any) : Boolean = { 
    |  stringHolder.equals (ot.asInstanceOf [Test].stringHolder) 
    |  } 
    | } 
    | 
    | def main (args : Array [String]) : Unit = { 
    |  val a = Test (new JStringHolder ("a")) 
    |  val b = Test (new JStringHolder ("b")) 
    |  val c = Test (new JStringHolder ("c")) 
    |  val l1 = List (a, b) 
    |  val l2 = List (b, c) 
    |  println (l1.diff (l2)) 
    |  println (l1.intersect (l2)) 
    |  println (l1.union (l2)) 
    | } 
    | } 
defined module ListTest 

scala> ListTest.main (null) 
List(a) 
List(b) 
List(a, b, b, c) 

第一個測試(JStringHolder(「b」))不是==第二個。