Exactly, in Java i couldn't even inline my functions, and had zero control over the underlying memory layout.
You could, manually :). Either way if they're hot, they're inlined.
One common trick for open-addressing maps in Java I don't see in your implementations is to have an array of keys zipped with values (or two arrays, one for keys, one for values) instead of array of Entries. This improves locality and indirection a bit. Obviously more is needed for even more speed.
(Mandatory "in the future it will be better": Valhalla is coming in a few years, that's when we'll have much more control over the memory layout.)
The tradeoffs are more complicated for a jit, but for an aot compiler it’s one of the most important optimisations in the pipeline.