Hey, welcome. Can you help me understand a bit mor...
# advent-of-code
t
Hey, welcome. Can you help me understand a bit more about what you are asking. Are you asking why you would do that or how to do it?
k
I think you need to do
m.getValue('f')
instead of
m['f']
for the default to work.
c
I am aware. Still, I don't get the reasoning behind that. I mean, the whole point of providing a
withDefault
is that I don't CARE if the key is in the map or not, I just want all keys to deliver a value, regardless. If I wanted to have to think about whether or not a key may or may not be in there and change all my map accesses to
getValue
, accordingly ... why bother? I could just as well use
getOrDefault
.
k
I think actually having
[]
work would vialolate the
Map
contract. What should
contains
do?
keySet()
?
valueSet()
, how does it know whether all possible keys are in there already? How should iteration work?
c
I'm not sure I understand the issue? The map has some underlying <Key,Value> store ... just search that for
contains
,
keySet
,
valueSet
, etc?
k
If
x !in map
then
map[x]
should return
null
.
That's pretty much what
contains
means.
c
I just checked and as far as I can see, map uses
Copy code
private fun implFindEntry(key: K): Map.Entry<K, V>? = entries.firstOrNull { it.key == key }
for its
containsKey
. Likewise,
Copy code
override fun containsValue(value: @UnsafeVariance V): Boolean = entries.any { it.value == value }
doesn't have to deal with get either. Now the issue seems to be that the internal
containsEntry
...
Copy code
val ourValue = get(key)

        if (value != ourValue) {
            return false
        }
        if (ourValue == null && !containsKey(key)) {
            return false
        }
does an optimization in that it only checks
containsKey
if the value is null. This could be rewritten to
Copy code
val ourValue = get(key)

        if (value != ourValue) {
            return false
        }
        if (!containsKey(key)) {
            return false
        }
in the
.withDefault
implementations, and voilà, "problem" solved. Since
containsEntry
is only ever used in the
equals
method, I'm willing to argue the extra overhead of having to search for keys with associated non-null values IF the value returned by
get
matches the expected value is negligible.
k
The problem is more fundamental than that: what do you want
x in map
to return for a map with default?
What do you want
keySet()
to return?
c
I still don't see the problem. I have a
Set
of entries ...
x in map
if that set contains a corresponding entry
keySet()
just iterate over the entries.
k
Well but a default map returns a value for each key, so by the map contract every possible key is "in" the map and so should be in the keySet.
c
That does sound like more of a philosophical question than an actual problem. I don't consider keys that don't have an entry to be IN the map, even if they return a default value.
I mean, that's what the default value is for, isn't it? Providing a non-null value for keys NOT in the map?
k
This is not some philosophical question, it's literally required by the
Map
specification: https://docs.oracle.com/javase/7/docs/api/java/util/Map.html#containsKey(java.lang.Object)
Returns true if this map contains a mapping for the specified key.
https://docs.oracle.com/javase/7/docs/api/java/util/Map.html#get(java.lang.Object)
Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.
"contains a mapping for the specified key" is defined by
contains
and
get
must respect it.
t
Karel, I never really gave that whole thing much thought but that makes perfect sense to me. Awesome.
👍 1
c
I agree, it makes sense, thank you, @karelpeeters. I DO feel a bit bad for wanting to break that contract, even now, though. 😅
k
I agree it's an annoying situation. You could make your own class
DefaultMap
that doesn't implement
Map
and does everything you wan of course.
c
"All problems in computer science can be solved by adding another layer of indirection", eh? I'm not particularly fond of the idea of giving up interchangability in favour of adherence to contracts. A map with default IS a map, for all intents and purposes, it's just nudging the contracts a bit.
(and yes, I know I'm making excuses 😝 )
k
I'd be very nervous to not adhere to the map contract when implementing a map, that stuff causes impossible to find bugs.
c
I know, it's just ... tempting. Kind of an "appel du vide", if you want.