Scala List.diff .intersect和.union无法按预期工作

时间:2011-11-16 11:14:34

标签: scala

使用以下代码产生以下内容:

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

我想要的当然是这样的结果:

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

我认为这是因为差异联合和交叉相对于“==”工作。 问题是“==” - 运算符是最终的,不能在“Test”类中​​重写。

我怎样才能达到预期的效果?

以下是使用过的代码:

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

4 个答案:

答案 0 :(得分:3)

问题是,你应该以某种方式覆盖hashCode方法,当你认为它们是相同的时,两个实例显示相同的hashCode。只有当hashCode相同时,才会调用equals方法,否则两个实例不一样(至少在List.diff意义上是相交的)。

使用下面的代码将达到我想要的结果:

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

答案 1 :(得分:1)

我不太确定:如果执行diff会出现错误?使用已弃用的--代替diff实际上对我有用。 Set代替List也可以。

我没有回答(只是建议使用Set作为解决方法)但我在这里添加自己的测试,以防他们可以帮助其他人回复:

// 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(scala 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)

答案 2 :(得分:0)

除了使用合理的equals方法将这些值封装在某些内容之外,您没有太多选择。

答案 3 :(得分: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(&#34; b&#34;))不是==第二个。