سؤال

Perhaps a simple to be answered question, but I did not find a satisfying answer from the API. I am not trying to write nice code, but I am trying to learn more on how certain things work:

I have created an initial HashMap. From an arbitrary list I would like to use map in order to create a list of HashMaps. I add a (key, value) pair to my HashMap within the .map function. Thus, in any successive index from this list the size of the stored HashMap should increase by one (so list(i).size == list(i+1).size - 1 for every i).

But with the following code snipped I get a list of Maps which are all equal (namely the full HashMap). If I print out the growing initial HashMap everything seems right, though.

scala> import scala.collection.mutable.HashMap

val m = new HashMap[Int, Int]

List(1,2,3,4) map {e =>
    println(m)
    val newM = m += e -> (e+2)
    newM
}
Map()
Map(1 -> 3)
Map(1 -> 3, 2 -> 4)
Map(3 -> 5, 1 -> 3, 2 -> 4)

import scala.collection.mutable.HashMap
m: scala.collection.mutable.HashMap[Int,Int] = Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4)
res7: List[scala.collection.mutable.HashMap[Int,Int]] = List(Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4), Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4), Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4),     Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4))

What I expected was something like

List(Map(1 -> 2), Map(1 -> 2, 2 -> 4), Map(1 -> 2, 2 -> 4, 3 -> 5), Map(1 -> 2, 2 -> 4, 3 -> 5, 4 -> 6))

The equivalent (I assume it is) version using a generative for loop results in exactly the same result:

scala> for {
    i <- 1 to 4
    val newM = m += i -> (i+2)
} yield newM
 res10: scala.collection.immutable.IndexedSeq[scala.collection.mutable.HashMap[Int,Int]] = Vector(Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4), Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4), Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4), Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4))

scala> for (i <- 1 to 4) yield {
    val newM = m += i -> (i+2)
    println(m)
    newM
} 
Map(1 -> 3)
Map(1 -> 3, 2 -> 4)
Map(3 -> 5, 1 -> 3, 2 -> 4)
Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4)
res11: scala.collection.immutable.IndexedSeq[scala.collection.mutable.HashMap[Int,Int]] = Vector(Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4), Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4), Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4), Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4))

I suppose I am missing a fundamental step, but I just do not see which. I appreciate any further help!

A subsequent question: Obviously the order in which the (key, value) pairs are printed out is always the same (though not in order of how they were elements, but I do not expect this from a Map anyways, this is natural). What happens in the background when the pairs are stored that this always seems to be the case?

هل كانت مفيدة؟

المحلول

m is a mutable HashMap.

The += method adds a new element to that very same map, and returns the map.

You have named the map newM, but this is a red herring! It's just the very same map m. So they're all just m.

If you really want a copy, you can call clone on m (e.g. val newM = (m += e -> (e+2)).clone).

Or, if you want to build up an immutable map as you go, you can use the scan method:

List(1,2,3,4).scanLeft(collection.immutable.HashMap[Int,Int]()){ (m,e) => m + (e -> (e+2)) }

which takes an item (in this case an empty map) and builds upon it step-by-step, returning the entire history to you when it's done:

res7: List[scala.collection.immutable.HashMap[Int,Int]]
  = List(Map(), Map(1 -> 3), Map(1 -> 3, 2 -> 4),
         Map(1 -> 3, 2 -> 4, 3 -> 5), Map(1 -> 3, 2 -> 4, 3 -> 5, 4 -> 6))

Better yet, you can

List(1,2,3,4).map(e => e -> (e+2)).toMap

to get your final map without worrying about the building process.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top