23

I think I may be failing to understand how mutable collections work. I would expect mutable collections to be affected by applying map to them or adding new elements, however:

scala> val s: collection.mutable.Seq[Int] = collection.mutable.Seq(1)
s: scala.collection.mutable.Seq[Int] = ArrayBuffer(1)

scala> s :+ 2 //appended an element
res32: scala.collection.mutable.Seq[Int] = ArrayBuffer(1, 2)

scala> s //the original collection is unchanged
res33: scala.collection.mutable.Seq[Int] = ArrayBuffer(1)

scala> s.map(_.toString) //mapped a function to it
res34: scala.collection.mutable.Seq[java.lang.String] = ArrayBuffer(1)

scala> s //original is unchanged
res35: scala.collection.mutable.Seq[Int] = ArrayBuffer(1)

//maybe mapping a function that changes the type of the collection shouldn't work
//try Int => Int

scala> s.map(_ + 1)
res36: scala.collection.mutable.Seq[Int] = ArrayBuffer(2)

scala> s //original unchanged
res37: scala.collection.mutable.Seq[Int] = ArrayBuffer(1)

This behaviour doesn't seem to be separate from the immutable collections, so when do they behave separately?

Henry Henrinson
  • 5,203
  • 7
  • 44
  • 76

4 Answers4

41

For both immutable and mutable collections, :+ and +: create new collections. If you want mutable collections that automatically grow, use the += and +=: methods defined by collection.mutable.Buffer.

Similarly, map returns a new collection — look for transform to change the collection in place.

serv-inc
  • 35,772
  • 9
  • 166
  • 188
Jean-Philippe Pellet
  • 59,296
  • 21
  • 173
  • 234
6

map operation applies the given function to all the elements of collection, and produces a new collection.

The operation you are looking for is called transform. You can think of it as an in-place map except that the transformation function has to be of type a -> a instead of a -> b.

scala> import collection.mutable.Buffer
import collection.mutable.Buffer

scala> Buffer(6, 3, 90)
res1: scala.collection.mutable.Buffer[Int] = ArrayBuffer(6, 3, 90)

scala> res1 transform { 2 * }
res2: res1.type = ArrayBuffer(12, 6, 180)

scala> res1
res3: scala.collection.mutable.Buffer[Int] = ArrayBuffer(12, 6, 180)
missingfaktor
  • 90,905
  • 62
  • 285
  • 365
1

The map method never modifies the collection on which you call it. The type system wouldn't allow such an in-place map implementation to exist - unless you changed its type signature, so that on some type Collection[A] you could only map using a function of type A => A.

(Edit: as other answers have pointed out, there is such a method called transform!)

Because map creates a new collection, you can go from a Collection[A] to a Collection[B] using a function A => B, which is much more useful.

Ben James
  • 121,135
  • 26
  • 193
  • 155
1

As others have noted, the map and :+ methods return a modified copy of the collection. More generally, all methods defined in collections from the scala.collection package will never modify a collection in place even when the dynamic type of the collection is from scala.collection.mutable. To modify a collection in place, look for methods defined in scala.collection.mutable._ but not in scala.collection._.

For example, :+ is defined in scala.collection.Seq, so it will never perform in-place modification even when the dynamic type is a scala.collection.mutable.ArrayBuffer. However, +=, which is defined in scala.collection.mutable.ArrayBuffer and not in scala.collection.Seq, will modify the collection in place.

DBear
  • 312
  • 2
  • 9