Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to copy a list in Scala

I want to shallow copy a list in Scala.

I wanted to do somehing like:

val myList = List("foo", "bar")
val myListCopy = myList.clone

But the clone method is protected.

like image 585
Maxime Avatar asked Sep 18 '09 16:09

Maxime


People also ask

How do I create a list in Scala?

Syntax for defining a Scala List. val variable_name: List[type] = List(item1, item2, item3) or val variable_name = List(item1, item2, item3) A list in Scala is mostly like a Scala array. However, the Scala List is immutable and represents a linked list data structure. On the other hand, Scala array is flat and mutable.

How do I add elements to a Scala list?

This is the first method we use to append Scala List using the operator “:+”. The syntax we use in this method is; first to declare the list name and then use the ':+' method rather than the new element that will be appended in the list. The syntax looks like “List name:+ new elements”.

What is difference between Array and list in Scala?

An Array is mutable, meaning you can change the values of each index, while a List (by default) is immutable, meaning that a new list is created every time you do a modification.


2 Answers

Here's a non-answer: don't do that. A List is immutable, so there's absolutely no point in copying one.

Let's consider a few operations:

val list = List(1,2,3)
val l1 = 0 :: list
val l2 = "a" :: list

Neither l1 nor l2 are altering list, but they both create new lists that reference list.

Let's explain this in detail. The constructor List(1,2,3) is creating three elements, and using a singleton object as well. Specifically, it is instantiating these elements:

::(3, Nil)
::(2, reference to the previous element)
::(1, reference to the previous element)

And Nil is a singleton object. What the identifier list actually points to is that last element.

Now, when you assign 0 :: list to l1, you are instantiating one new object:

::(0, reference to ::(1, etc))

Of course, since there's a reference to list, you can think of l1 as a list of four elements (or five, if you count Nil).

Now l2 isn't even of the same type of list, but it ALSO references it! Here:

::("a", reference to ::(1, etc))

The important point about all these objects, though, is that they can't be changed. There are no setters, nor any methods that will change any of their properties. They'll be forever having the same values/reference in their "head" (that's what we call the first element), and the same references in their "tail" (that's what we call the second element).

However, there are methods that look like their are changing the list. Rest assured, however, that they are creating new lists. For instance:

val l3 = list map (n => n + 1)

The method map creates a completely new list, of the same size, where new element may be computed from a corresponding element in list (but you might ignore the old element as well).

val l4 = l2 filter (n => n.isInstanceOf[Int])

While l4 has the same elements as list (but a different type), it is also a completely new list. The method filter creates a new list, based on a rule you pass to tell it what elements go in and what don't. It doesn't try to optimize in case it could return an existing list.

val l5 = list.tail

This does not create a new list. Instead, it simply assigns to l5 an existing element of list.

val l6 = list drop 2

Again, no new list created.

val l7 = list take 1

This, however, creates a new list, precisely because it can't change the first element of list so that its tail points to Nil.

Here's a few additional implementation details:

  • List is an abstract class. It has two descendants, the class :: (yes, that's the name of the class), and the singleton object Nil. List is sealed, so you can't add new subclasses to it, and :: is final, so you can't subclass it.

  • While you can't do anything to change a list, it uses mutable state internally in some operations. This helps with performance, but it is localized so that no program you write can ever detect it, or suffer consequences from it. You can pass lists around as you wish, no matter what the other functions do with them, or how many threads are using them concurrently.

like image 82
Daniel C. Sobral Avatar answered Sep 18 '22 04:09

Daniel C. Sobral


To filter a list:

val list = List(1,2,3,4,5)
//only evens
val evens = list.filter(e=>e%2 == 0)

println(list)
//--> List(1, 2, 3, 4, 5)

println(evens) 
//--> List(2, 4)

You can also use the wildcard to save a few characters:

val evens = list.filter(_%2==0)

Note that, as commented above, lists are immutable. That means that these operations do not modify the original list, but actually create a new list.

like image 41
Jon Hoffman Avatar answered Sep 21 '22 04:09

Jon Hoffman