I suspect this question was asked multiple times, ...
# stdlib
d
I suspect this question was asked multiple times, but it gets me every time and I can't google right away: Having a
MutableMap<T, MutableList<R>>
is there a one liner to put an
R
into the existing list OR create a list with a single
R
, something like
Copy code
// when map is {}
map.magicOp("one", 3) // → { one: [3] }
map.magicOp("one", 4) // → { one: [3,4] }
w
map.getOrPut
?
d
Copy code
map.getOrPut("one", { mutableListOf() }).add(3)
map.getOrPut("one", { mutableListOf() }).add(4)
?
👍 1
seems like it's what I need, thanks. forgot this again)
👍 1
s
There is also a way to get a map with a default. Cant remember how but might be simpler if you end up calling
getOrPut
many places
d
yeah, iirc
withDefault
w
Yep, but it doesn’t put the default value into the map when requested, if I recall correctly
s
Ah right
e
can move the lambda outside the parens,
map.getOrPut("one") { mutableListOf() }
if you're targeting Java 8, you can use
map.computeIfAbsent("one") { mutableListOf() }
which will be properly atomic on ConcurrentMap
👍 1
f
can move the lambda outside the parens,
map.getOrPut("one") { mutableListOf() }
Or not at all
map.getOrPut("one", ::mutableListOf)
🙂
👌 1
e
true. there are situations where
::
doesn't work due to overload resolution, so I tend to use
{ }
consistently everywhere. that's an opinion of personal style, though
f
After the first JVM optimization the bytecode ends up the same, so all variations are equal. And it's exactly as you say, type inference continues to be confused in various situations.
e
they actually generate slightly different bytecode,
::
returns a
kotlin.reflect.KFunction
which has some extra methods and interfaces (e.g.
::error.name == "error"
,
{ error(it) }.name
does not compile)
f
Copy code
fun main() {
    mutableMapOf<String, List<String>>().getOrPut("k", ::mutableListOf)
}
Gives:
Copy code
boolean var0 = false;
      Map $this$getOrPut$iv = (Map)(new LinkedHashMap());
      Object key$iv = "k";
      int $i$f$getOrPut = false;
      Object value$iv = $this$getOrPut$iv.get(key$iv);
      if (value$iv == null) {
         int var4 = false;
         boolean var5 = false;
         Object answer$iv = (List)(new ArrayList());
         $this$getOrPut$iv.put(key$iv, answer$iv);
      }
::
does not act as reflection accessor in this case, it's a function reference and compiler instruction.
Copy code
public fun main() {
    mutableMapOf<String, List<String>>().getOrPut("k") { mutableListOf() }
}
In this particular case Kotlin does even produce the same bytecode right away:
Copy code
boolean var0 = false;
      Map $this$getOrPut$iv = (Map)(new LinkedHashMap());
      Object key$iv = "k";
      int $i$f$getOrPut = false;
      Object value$iv = $this$getOrPut$iv.get(key$iv);
      if (value$iv == null) {
         int var4 = false;
         boolean var5 = false;
         Object answer$iv = (List)(new ArrayList());
         $this$getOrPut$iv.put(key$iv, answer$iv);
      }
So, it really is up to personal taste how you write it here, and that's fine. 🙂
e
yes, when it's inlined (as in the case of
.getOrPut()
, but not in the case of
.computeIfMissing()
), the bytecode is the same. when it's not inline, it's not quite the same
it almost never matters, although the difference between the two does have some implications for how to migrate Kotlin to the JVM's invokedynamic instruction (see https://youtrack.jetbrains.com/issue/KT-26060 and https://youtrack.jetbrains.com/issue/KT-45658)
👍 1