Checking if two map variables refer to the same map

1,555 views
Skip to first unread message

Jsor

unread,
Jul 13, 2013, 11:35:55 AM7/13/13
to golan...@googlegroups.com
I ask for maps because for slices this seems potentially problematic: what does "same reference" entail for a slice? Overlapping underlying arrays? Same starting pointer regardless of whether their len matches? Same start, end, len, and cap? And so on. Though I guess "reference-equality" would be pretty well defined for channels.

However, for maps determining "sameness" at a reference level seems like a much more well defined question, and a much simpler one to answer. Yet I can't figure out a good way to do it. Perhaps with reflect.Value.UnsafePointer (would that even work)? Either way, that seems like overcomplicating things. The "easiest" way to do it seems to be something like this, dreamt up on the go-nuts IRC when I asked this: http://play.golang.org/p/6Ffxfx7zBb

But I think we can all agree that that's a rather silly and limited solution (and to be fair wasn't suggested in earnest).

I can see why == isn't defined on maps, too many people would likely mistake it for a deep equality test (if that was indeed the reason), but it seems like there should be some semi-trivial way to see if two map variables refer to the same map. Perhaps a need just wasn't seen for such an operation? Maybe it's really a more difficult/expensive test than I assumed?

Dave Cheney

unread,
Jul 13, 2013, 11:53:29 AM7/13/13
to Jsor, golan...@googlegroups.com
Do you see a need for this operation? Can you provide a sample of code what you need to find out if two map values are the same map?
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Jsor

unread,
Jul 13, 2013, 12:01:59 PM7/13/13
to golan...@googlegroups.com, Jsor
Sure (both functions assume both arguments are non-nil just for brevity),

func CopyTo(m1, m2 map[int]int) {
  if m1 ReallyEquals m2 {
     return
  }

  for key,val := range map1 {
    m2[key] = val
  }
}

func IsEqual(m1, m2 map[int]int) bool {
  if m1 ReallyEquals m2 {
    return true // trivial case
  } else if len(m1) != len(m2) {
     return false
  }

  for key,val := range m1 {
     if val != m2[key] {
        return false
     }
  }
  return true
}

In both cases it could potentially save a lot of pointless loops, comparisons, and/or copies. The exception might be if the standard compilers make this optimization already, in which case it's a moot point.

Dave Cheney

unread,
Jul 13, 2013, 12:05:48 PM7/13/13
to Jsor, golan...@googlegroups.com, Jsor
Respectfully, you haven't shown a problem that cannot be solved without map value equality. You just wrote two functions that would work, if map values defined an equality operator. 

Jsor

unread,
Jul 13, 2013, 12:15:06 PM7/13/13
to golan...@googlegroups.com, Jsor
True, I can't think of a scenario where it is *required*. However, if you have, say, a Set implementation (most easily done with a map), it's not uncommon to have very large sets -- and it seems rather needless to force iterating over a 10k member set just to prove that a reference is, indeed, equal to itself. Again, if there is some under-the-hood optimization that does something like this then whether such an operation exists or not isn't really a big deal. It's mostly just a performance related concern.

I'm not asking for an "=="-like operator to be added to the built-ins for maps. I was mostly curious if there was some way to do this with the standard library, hopefully avoiding reflect, but unsafe would likely be fine if it's the only way.

Rémy Oudompheng

unread,
Jul 13, 2013, 12:21:21 PM7/13/13
to Jsor, golang-nuts
On 2013/7/13 Jsor <jrago...@gmail.com> wrote:
> True, I can't think of a scenario where it is *required*. However, if you
> have, say, a Set implementation (most easily done with a map), it's not
> uncommon to have very large sets -- and it seems rather needless to force
> iterating over a 10k member set just to prove that a reference is, indeed,
> equal to itself. Again, if there is some under-the-hood optimization that
> does something like this then whether such an operation exists or not isn't
> really a big deal. It's mostly just a performance related concern.
>
> I'm not asking for an "=="-like operator to be added to the built-ins for
> maps. I was mostly curious if there was some way to do this with the
> standard library, hopefully avoiding reflect, but unsafe would likely be
> fine if it's the only way.

Why do you want to use unsafe? As the name says, it is unsafe whereas
reflect is allowed in safe contexts such as the playground:

http://play.golang.org/p/XiHmNxuTYQ

package main

import "reflect"
import "fmt"

func main() {
m := make(map[int]int)
fmt.Printf("0x%x\n", reflect.ValueOf(m).Pointer())
}

Rémy.

Jeff Juozapaitis

unread,
Jul 13, 2013, 12:24:06 PM7/13/13
to Rémy Oudompheng, golang-nuts
Well, I'm not wholly opposed to reflect, but I understand that ValueOf is fairly costly. I'm not clear on how costly it is though, maybe it's okay?

Jim Robinson

unread,
Jul 13, 2013, 12:29:08 PM7/13/13
to golan...@googlegroups.com, Jsor
On Saturday, July 13, 2013 5:15:06 AM UTC-7, Jsor wrote:
If you have, say, a Set implementation (most easily done with a map)

Probably it's considered easy enough to define your own Set struct
that holds a private map and a public Id.  I would guess that might
also be a bit easier to understand for another person reading the
code (distinct types for a variable vs. using a the generic map/slice).

Jim

Dave Cheney

unread,
Jul 13, 2013, 12:34:33 PM7/13/13
to Jim Robinson, golang-nuts, Jsor
If you are defining a Set type, say

type set Struct {
mu sync.Mutex
values map[something]struct{}
}

then two structs are identical if their addresses are identical, and
thus so are the maps they contain.

atomly

unread,
Jul 13, 2013, 8:48:45 PM7/13/13
to Jeff Juozapaitis, Rémy Oudompheng, golang-nuts
No offense, but you've contrived a problem where you yourself admit that you have no real need to solve it and then complain that a perfectly valid solution to it might possibly be too inefficient-- talk about a new level of premature optimization! The only "sane," safe and accurate (your SECRET example could, though unlikely, easily give false positives) way to do this is via reflect.

:: atomly ::

[ ato...@atomly.com : www.atomly.com  : http://blog.atomly.com/ ...
[ atomiq records : new york city : +1.347.692.8661 ...
[ e-mail atomly-new...@atomly.com for atomly info and updates ...


On Sat, Jul 13, 2013 at 8:24 AM, Jeff Juozapaitis <jrago...@gmail.com> wrote:
Well, I'm not wholly opposed to reflect, but I understand that ValueOf is fairly costly. I'm not clear on how costly it is though, maybe it's okay?

--

atomly

unread,
Jul 13, 2013, 8:54:03 PM7/13/13
to Jeff Juozapaitis, Rémy Oudompheng, golang-nuts
(If you wanted the SECRET version to be safe, you'd have to generate a random key, check that it doesn't exist in either map (otherwise generate a new random key and try again), then set it in a, check that it exists in b and then, finally, delete it from one or both maps. As it's written, your current version would falsely return true if run twice for any two maps.)

:: atomly ::

[ ato...@atomly.com : www.atomly.com  : http://blog.atomly.com/ ...
[ atomiq records : new york city : +1.347.692.8661 ...
[ e-mail atomly-new...@atomly.com for atomly info and updates ...


David DENG

unread,
Jul 15, 2013, 12:50:01 AM7/15/13
to golan...@googlegroups.com
There're always something that is not comparable. You can consider map as one of this. If you have to check, use the pointer form.

David

mi...@daglabs.com

unread,
Aug 7, 2018, 1:34:03 PM8/7/18
to golang-nuts
Sorry for bumping a very old thread, but I absolutely disagree with the people stating that this problem is contrived, and I got here from a Google search, so this might be relevant for some people.

A very real use-case for reference-comparing maps is when testing .Clone() methods. You want to make sure that the clone is an actual clone, and that all the properties of the cloned object are also a clone, etc. In these cases you want to reference-compare everything.

That said, reflect.ValueOf(xxx).Pointer is more than sufficient for this use-case.

Kevin Regan

unread,
Feb 10, 2020, 11:42:39 PM2/10/20
to golang-nuts
I just ran into this... ...makes me like go a little less.

roger peppe

unread,
Feb 11, 2020, 1:51:27 PM2/11/20
to Kevin Regan, golang-nuts
I believe that the main reason that equality isn't defined on maps (and slices) is to preserve the future possibility that equality might work at a whole-value level rather than on a reference level. I suspect that one of these days a final decision will be made...


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Kevin Regan

unread,
Feb 11, 2020, 11:56:05 PM2/11/20
to roger peppe, golang-nuts
Would something like this work:

m1 := make(map[string]string)
m2 := m1

if &m1 == &m2 {
   ...
}

Ian Lance Taylor

unread,
Feb 12, 2020, 4:10:33 AM2/12/20
to roger peppe, Kevin Regan, golang-nuts
On Tue, Feb 11, 2020 at 5:51 AM roger peppe <rogp...@gmail.com> wrote:
>
> I believe that the main reason that equality isn't defined on maps (and slices) is to preserve the future possibility that equality might work at a whole-value level rather than on a reference level. I suspect that one of these days a final decision will be made...

I would say that slightly differently. Equality of reference types is
ambiguous. Does slice or map equality mean equality of reference or
equality of value? Different programs want different choices; neither
is obviously correct. So the language doesn't make a choice.

Ian
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAJhgacgt5ddXTGEr5nXxS3UCxRqcBkzZcyhYU70jbcejT0r87A%40mail.gmail.com.

Ian Lance Taylor

unread,
Feb 12, 2020, 4:12:52 AM2/12/20
to Kevin Regan, roger peppe, golang-nuts
On Tue, Feb 11, 2020 at 3:55 PM 'Kevin Regan' via golang-nuts
<golan...@googlegroups.com> wrote:
>
> Would something like this work:
>
> m1 := make(map[string]string)
> m2 := m1
>
> if &m1 == &m2 {
> ...
> }

That would do something but it's not a particularly interesting
property. I'm not sure what you are trying to check.

If you want to test whether two variables of map type have equality of
reference--whether both variables refer to the same map--you can write
reflect.Value(m1).Pointer() == reflect.Value(m2).Pointer()

Ian
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAG%3Dmn_s25XcHKhOdPuZCDFMLM1v1O0z7S4i5wan9ZXiPCKVmsA%40mail.gmail.com.

Jake Montgomery

unread,
Feb 12, 2020, 4:11:44 PM2/12/20
to golang-nuts
The playground is your friend: https://play.golang.org/p/p10XugRD4_z
So, the answer is no.
To unsubscribe from this group and stop receiving emails from it, send an email to golan...@googlegroups.com.

dtra...@gmail.com

unread,
Mar 14, 2020, 1:47:20 AM3/14/20
to golang-nuts
I have a use for this that is not covered it seems.

So basically I am runnning a code that may or may not create a map depending on if it needs to fill it with items.
However I always need a map that is initialized in future downpath code.  ( i use it in a range statement that just noops if it is empty)
This code has to be optimized in the initial creation.  less so for when i use it.


So the goal i was looking for was to always set a shared instance "Empty" map
then when i need to fill it i check if the value is the empty map pointer and if so replace it with a brand new instance.
That way all instances are lazy created and saves memory.
This would be happening hundreds of thousands of times so even small performance improvements end up looking big at the end.

if somebody has an alternative lazy initialization method that does not require pointer checks then i can use that instead.

roger peppe

unread,
Mar 14, 2020, 7:56:54 AM3/14/20
to dtra...@gmail.com, golang-nuts
In this particular case, couldn't you just check for len(m) == 0 ? i.e. assume that any zero size map is the shared one?

To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/4c7dae4c-0420-485e-93f4-d13d6497a379%40googlegroups.com.

Axel Wagner

unread,
Mar 14, 2020, 1:39:51 PM3/14/20
to dtra...@gmail.com, golang-nuts
On Sat, Mar 14, 2020 at 2:47 AM <dtra...@gmail.com> wrote:
However I always need a map that is initialized in future downpath code.  ( i use it in a range statement that just noops if it is empty)

To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/4c7dae4c-0420-485e-93f4-d13d6497a379%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages