Skip to content

Inconsistent hashCode with mapValues #10863

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Robert-Morewood opened this issue May 4, 2018 · 8 comments
Closed

Inconsistent hashCode with mapValues #10863

Robert-Morewood opened this issue May 4, 2018 · 8 comments

Comments

@Robert-Morewood
Copy link

Robert-Morewood commented May 4, 2018

The following mapValues snippet might be expected to return true:
val aMap = Map(0->0).mapValues(y => ((x: Int) => x + y)); aMap.hashCode == aMap.hashCode
However it returns false where I have tested it:

  • Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_131)
  • Scala 2.12.5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_112)
    (A trivialized example of a problem that did affect our production code.)

The above behaviour violates the Java documentation on hashCode : https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#hashCode--

Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer

For anyone else bothered by this behaviour, applying .view.force to the mapValues result produces a real map with a consistent hashCode, at the cost of evaluating and storing the mapValues expression for every value in the original map.

@NthPortal
Copy link

(@Robert-Morewood I think you need a line break before aMap.hashCode == aMap.hashCode; as it is now, it's not syntactically valid)

@NthPortal
Copy link

For some reason, the lambda in the map seems to be generated anew each time you do something with the map

@hrhino
Copy link

hrhino commented May 4, 2018

This is probably another consequence of mapValues being lazy: it does make a new closure every time.

@Robert-Morewood
Copy link
Author

@NthPortal Thanks for the syntax correction! I had a semi-colon in my test code...

@hrhino I suspect that mapValues is misreprented. That lazy evaluation effectively makes it a View rather than a Map, but hashCode is treating it as the Map it claims to be. If we instead do:
val aMap = Map(0->0).mapValues(y => ((x: Int) => x + y)).view; aMap.hashCode == aMap.hashCode
then hashcode works.

@som-snytt
Copy link

That's an interesting spin on an old chestnut.

The canonical issue is #4776.

There was just #10663 about hashCode when converting.

I was a little surprised that converting doesn't force; in the sense that nothing surprises me.

scala> val m = Map(0 -> 0).mapValues(_ => new Object)
m: scala.collection.immutable.Map[Int,Object] = Map(0 -> java.lang.Object@3c380bd8)

scala> m
res0: scala.collection.immutable.Map[Int,Object] = Map(0 -> java.lang.Object@45117dd)

scala> import scala.collection.JavaConverters._
import scala.collection.JavaConverters._

scala> val j = m.asJava
j: java.util.Map[Int,Object] = {0=java.lang.Object@4bbf38b8}

scala> j
res1: java.util.Map[Int,Object] = {0=java.lang.Object@26a45089}

@SethTisue SethTisue added this to the 2.13.0-M4 milestone May 5, 2018
@SethTisue
Copy link
Member

already fixed in the new collections, which are coming in 2.13.0-M4. closing because I doubt this can be fixed in the 2.12.x series without breaking compatibility (although if someone has an idea of how to do that we could reopen, I haven't thought deeply about it)

in the new collections, .mapValues returns a scala.collection.MapView

@NthPortal
Copy link

NthPortal commented May 5, 2018

A cheap fix/mitigation would be to tweak the equals method to try eq first. It doesn't seem to me like that should break compatibility, but I'm not an expert in that area.

Edit: I'm not sure why I thought this would help with hashCode

@som-snytt som-snytt changed the title Inconsistent hashMap with mapValues Inconsistent hashCode with mapValues May 5, 2018
@som-snytt
Copy link

@NthPortal I updated the title to help us recall what we were talking about, in case it comes up later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants