5

I'm looking for as simple way to create an identity set. I just want to be able to keep track of whether or not I've "seen" a particular object while traversing a graph.

I can't use a regular Set because Set uses "==" (the equals method in Scala) to compare elements. What I want is a Set that uses "eq."

Is there any way to create a Set in Scala that uses some application-specified method for testing equality rather than calling equals on the set elements? I looked for some kind of "wrapEquals" method that I could override but did not find it.

I know that I could use Java's IdentityHashMap, but I'm looking for something more general-purpose.

Another idea I had was to just wrap each set element in another object that implements equals in terms of eq, but it's wasteful to generate tons of new objects just to get a new equals implementation.

Thanks!

Eugene Yokota
  • 94,654
  • 45
  • 215
  • 319
Willis Blackburn
  • 8,068
  • 19
  • 36

3 Answers3

3

Depending on your needs you could create a box for which you use identity checks on the contained element such as:

class IdentBox[T <: AnyRef](val value: T) {

    override def equals(other: Any): Boolean = other match {
      case that: IdentBox[T] => that.value eq this.value
      case _ => false
    }

    override def hashCode(): Int = value.hashCode

  }

And make the collection to contain those boxes instead of the elements directly: Set[IdentBox[T]]

It has some overhead of boxing / unboxing but it might be tolerable in your use case.

Miquel
  • 4,741
  • 3
  • 20
  • 19
  • The `hashCode` you have provided works (actually any hash would work as long as it returns the same value for the same instance), but perhaps `System.identityHashCode(value)` would work better? – Suma Jan 05 '18 at 14:25
  • I am unsure about this given the comment by Willis Blackburn in another answer of this question: Unfortunately System.identityHashCode returns the default hash code value, not the address, so two different objects can have the same identityHashCode value. – Miquel Jan 07 '18 at 10:25
  • The `identityHashCode` is a 32b bit value (`int`), therefore it is not (and cannot be) guaranteed to be unique on 64b JVM. Still, it is most likely more unique than a `hashValue` which was designed to cooperate with the `case class` value equality, which is what you are using now. In your case anything works, as you would not use `identityHashCode` to perform the comparison, only to serve as a hash to accompany `eq` based comparison. – Suma Jan 07 '18 at 15:38
1

This is a similar question. The accepted answer in that case was to use a TreeSet and provide a custom Comparator.

Community
  • 1
  • 1
Ben Lings
  • 28,823
  • 13
  • 72
  • 81
-1

Since you don't require a reference to the "seen" objects, but just a boolean value for "contains", I would suggest just using a mutable.Set[Int] and loading it with values obtained by calling System.identityHashCode(obj).

Scala custom collections have enough conceptual surface area to scare off most people who want a quick tweak like this.

Mitch Blevins
  • 13,186
  • 3
  • 44
  • 32
  • 3
    Unfortunately System.identityHashCode returns the default hash code value, not the address, so two different objects can have the same identityHashCode value. It might work in a 32-bit JVM, but in a 64-bit JVM, there would have to be some clashes. – Willis Blackburn Apr 19 '10 at 10:41
  • 1
    This is only a problem if you want your software to work _every_ time. :-) – Mitch Blevins Apr 19 '10 at 13:41