web-dev-qa-db-fra.com

Comment puis-je définir une opération d'égalité personnalisée qui sera utilisée par des méthodes de comparaison Set immuables

J'ai un ensemble immuable d'une classe, Set [MyClass], et je veux utiliser les méthodes Set intersect et diff, mais je veux qu'ils testent l'égalité à l'aide de ma méthode égale personnalisée, plutôt que le test d'égalité d'objet par défaut

J'ai essayé de remplacer l'opérateur ==, mais il n'est pas utilisé.

Merci d'avance.

Éditer:

La méthode d'intersection est un membre de valeur concrète de GenSetLike

spec: http://www.scala-lang.org/api/current/scala/collection/GenSetLike.html src: https://lampsvn.epfl.ch/trac/ scala/browser/scala/tags/R_2_9_1_final/src // library/scala/collection/GenSetLike.scala # L1

def intersect(that: GenSet[A]): Repr = this filter that

donc l'intersection se fait en utilisant la méthode du filtre.

Encore un autre Edit:

le filtre est défini dans TraversableLike

spec: http://www.scala-lang.org/api/current/scala/collection/TraversableLike.html

src: https://lampsvn.epfl.ch/trac/scala/browser/scala/tags/R_2_9_1_final/src//library/scala/collection/TraversableLike.scala#L1

def filter(p: A => Boolean): Repr = {
  val b = newBuilder
      for (x <- this) 
        if (p(x)) b += x
      b.result
}

Ce qui n'est pas clair pour moi, c'est ce qu'il utilise lorsqu'il est invoqué sans prédicat, p. Ce n'est pas un paramètre implicite.

43
Kareem

equals et hashCode sont fournis automatiquement dans la classe case uniquement si vous ne les définissez pas.

case class MyClass(val name: String) {
  override def equals(o: Any) = o match {
    case that: MyClass => that.name.equalsIgnoreCase(this.name)
    case _ => false
  }
  override def hashCode = name.toUpperCase.hashCode
}

Set(MyClass("xx"), MyClass("XY"), MyClass("xX"))
res1: scala.collection.immutable.Set[MyClass] = Set(MyClass(xx), MyClass(XY))

Si vous voulez une égalité de référence, écrivez toujours equals et hashCode, pour empêcher la génération automatique, et appelez la version depuis AnyRef

  override def equals(o: Any) = super.equals(o)
  override def hashCode = super.hashCode

Avec ça:

Set(MyClass("x"), MyClass("x"))
res2: scala.collection.immutable.Set[MyClass] = Set(MyClass(x), MyClass(x))

Vous ne pouvez pas remplacer le ==(o: Any) d'AnyRef, qui est scellé et appelle toujours égal. Si vous avez essayé de définir un nouveau (surchargé) ==(m: MyClass), ce n'est pas celui que Set appelle, donc il est inutile ici et assez dangereux en général.

Quant à l'appel à filter, la raison pour laquelle cela fonctionne est que Set[A] Est un Function[A, Boolean]. Et oui, equals est utilisé, vous verrez que l'implémentation de la fonction (apply) est synonyme de contains, et la plupart des implémentations de Set utilisent == Dans contient (SortedSet utilise le Ordering à la place). Et == Appelle equals.


Remarque: l'implémentation de mon premier equals est rapide et sale et probablement mauvaise si MyClass doit être sous-classée. Si c'est le cas, vous devriez au moins vérifier l'égalité des types (this.getClass == that.getClass) Ou mieux définir une méthode canEqual (vous pouvez lire ce blog par Daniel Sobral)

50
Didier Dupont

Vous devrez remplacer .hashCode ainsi que. C'est presque toujours le cas lorsque vous remplacez .equals, comme .hashCode est souvent utilisé comme pré-contrôle moins cher pour .equals; deux objets égaux doivent avoir des codes de hachage identiques. Je suppose que vous utilisez des objets dont la valeur par défaut hashCode ne respecte pas cette propriété en ce qui concerne votre égalité personnalisée, et l'implémentation de Set fait des hypothèses basées sur les codes de hachage (et donc jamais même en appelant votre opération d'égalité ).

Voir les documents Scala pour Any.equals et Any.hashCode: http://www.scala-lang.org/api/rc/scala/Any.html

8
Ben

Cette réponse montre un ensemble mutable personnalisé avec une égalité définie par l'utilisateur . Il pourrait être rendu immuable en remplaçant le magasin interne par un Vector et en retournant une copie modifiée de lui-même à chaque opération

1
Ben Hutchison

"Il n'est pas possible de remplacer directement ==, car il est défini comme méthode finale dans la classe Any. C'est-à-dire Scala traite == comme s'il était défini comme suit dans la classe Any:

    final def == (that: Any): Boolean =
      if (null eq this) {null eq that} else {this equals that}

"de Programming In Scala, deuxième édition

0
t. fochtman