2017-09-25 4 views
1

関数がScalaのListとTreeSetの間で動作が異なるように見える奇妙なケースに遭遇しましたが、それを解決する理由や方法がわかりません。ListsとTreeSetで異なる動作をする

簡潔にするため、DataStructureというクラスを作成しました。これには、座標ペア(i, j)Intという2つの要素が含まれています。 (これよりも複雑ですが、このMWEではこれが見えます)Intでソートされるカスタムコンパレータがあり、hashCodeequalsをオーバーライドして、同じ座標ペア(i, j)を含む2つの要素が処理されますIntにかかわらず等しいとします。私は両方のListTreeSetDataStructureのインスタンスを入れると

は、プログラムが完全一致を見つける問題はありません。ただし、同じ座標ペアであるが、Intが異なる新しい要素を確認する場合、List.containstrueを返し、TreeSet.containsfalseを返します。なぜこれが起こり、どのように解決できますか?

これは最低限の作業例に減少私のコードです:

クラスDataStructure

package foo 

class DataStructure(e1: (Int, Int), e2: Int) extends Ordered[DataStructure] { 
    val coord: (Int, Int) = e1 
    val cost: Int = e2 

    override def equals(that: Any): Boolean = { 
    that match { 
    case that: DataStructure => if (that.coord.hashCode() == this.coord.hashCode()) true else false 
    case _ => false 
    }} 
    override def hashCode(): Int = this.coord.hashCode() 

    def compare(that: DataStructure) = { 
    if (this.cost == that.cost) 
     0 
    else if (this.cost > that.cost) 
     -1 //reverse ordering 
    else 
     1 
    }  
} 

ドライバプログラム

package runtime 

import foo.DataStructure 
import scala.collection.mutable.TreeSet 

object Main extends App { 
     val ts = TreeSet[DataStructure]() 

     val a = new DataStructure((2,2), 2) 
     val b = new DataStructure((2,3), 1) 

     ts.add(a) 
     ts.add(b) 

     val list = List(a, b) 

     val listRes = list.contains(a) // true 
     val listRes2 = list.contains(new DataStructure((2,2), 0)) // true 
     val tsRes = ts.contains(a) // true 
     val tsRes2 = ts.contains(new DataStructure((2,2), 0)) // FALSE! 

     println("list contains exact match: " + listRes) 
     println("list contains match on first element: " + listRes2) 
     println("TreeSet contains exact match: " + tsRes) 
     println("TreeSet contains match on first element: " + tsRes2) 
} 

出力:

list contains exact match: true 
list contains match on first element: true 
TreeSet contains exact match: true 
TreeSet contains match on first element: false 
+2

あなたはすでに答えを持っていますが、異なるオブジェクトに等しいハッシュコードを持つことができるので、hashcodeをequalsとして使用することは悪い考えです。 – puhlen

答えて

7

アルモ確かにList.containsは一致するものを見つけるために各要素のためにequalsをチェックしていますが、TreeSet.containsはツリーを歩いており、compareを使って一致するものを見つけています。

compareとあなたのequalsとの一貫性がないという問題があります。私は、あなたがそれをやっている理由がわからない、ではないんです。「順序付きのインスタンスのequalsメソッドは、[A]の比較方法と一致していることが重要である」

https://www.scala-lang.org/api/current/scala/math/Ordered.html

+1

バニラJavaであっても、equalsとcompareToの間の整合性は、equalsとhashCodeの間の整合性がObjectコントラクトの一部であるため、Comparableコントラクトの一部です。この場合、3は一貫していなければなりません – cchantep

関連する問題