Как определить пользовательскую операцию равенства, которая будет использоваться неизменными методами сравнения Set? - PullRequest
43 голосов
/ 07 октября 2011

У меня есть неизменный набор класса Set [MyClass], и я хочу использовать методы Set intersect и diff, но я хочу, чтобы они проверяли равенство, используя мой пользовательский метод equals, а не тест на равенство объектов по умолчанию

Я попытался переопределить оператор ==, но он не используется.

Заранее спасибо.

Редактировать:

Метод пересеченияконкретный элемент значения GenSetLike

spec: http://www.scala -lang.org / api / current / scala / collection / GenSetLike.html источник: 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

поэтому пересечение выполняется методом фильтра.

Еще один фильтр Edit:

определен в 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
}

Что мне неясно, что он использует при вызове без предиката, п.Это не неявный параметр.

Ответы [ 4 ]

50 голосов
/ 07 октября 2011

равно и hashCode предоставляются автоматически в случае класса, только если вы не определяете их.

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

Если вам нужно ссылочное равенство, все равно напишите equals и hashCode, чтобы предотвратить автоматическую генерацию, и вызовите версию из AnyRef

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

С этим:

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

Вы не можете переопределить ==(o: Any) из AnyRef, который запечатан и всегда вызывает равно. Если вы попытались определить новый (перегруженный) ==(m: MyClass), это не тот, который вызывает Set, поэтому он здесь бесполезен и в целом довольно опасен.

Что касается вызова на filter, то причина его работы в том, что Set[A] - это Function[A, Boolean]. И да, используется equals, вы увидите, что реализация функции (apply) является синонимом для contains, и большинство реализаций Set использует == в содержит (SortedSet использует Ordering вместо). И == звонит equals.


Примечание: реализация моего первого equals быстрая и грязная и, вероятно, плохая, если MyClass должен быть разделен на подклассы. Если это так, вы должны как минимум проверить равенство типов (this.getClass == that.getClass) или лучше определить метод canEqual (вы можете прочитать этот блог Даниэля Собрала)

8 голосов
/ 07 октября 2011

Вам также необходимо переопределить .hashCode.Это почти всегда тот случай, когда вы переопределяете .equals, так как .hashCode часто используется в качестве более дешевой предварительной проверки для .equals;любые два объекта, равные , должны иметь одинаковые хеш-коды.Я предполагаю, что вы используете объекты, чье значение по умолчанию hashCode не учитывает это свойство по отношению к вашему пользовательскому равенству, а реализация Set делает предположения на основе хеш-кодов (и поэтому никогда даже не вызывает вашу операцию равенства).

См. Документы Scala для Any.equals и Any.hashCode: http://www.scala -lang.org / api / rc / scala / Any.html

1 голос
/ 10 мая 2015

Этот ответ показывает пользовательский изменяемый набор с пользовательским равенством .Его можно сделать неизменным, заменив внутреннее хранилище на Vector и возвращая измененную копию себя при каждой операции

0 голосов
/ 26 июля 2014

"Невозможно переопределить == напрямую, поскольку он определен как последний метод в классе Any. То есть Scala обрабатывает ==, как если бы он был определен в классе Any следующим образом:

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

"из программирования в Scala, второе издание

...