How to Append and Prepend to Scala List and other common list operations

Scala List ( scala.collection.immutable) represents an ordered collections of elements of type T.That means, the list expects every item to be of same type. This ensures strong type safety and avoids potential run time errors related to wrong type cast.

It is important to understand the performance implications of using the list , particularly when storing large number of types. List has O(1) prepend and head/tail access. This is quite good and hence a good option to add an item to the list is to prepend to it. The othe operations like index based access , appending to the list etc has O(n) times. This means the performance of those operations can degrade when the list grows. Keep this in mind if you are creating a list for storing huge number of items.

Now let us look at some of the common List operations.

Create a Scala List

The most common way to create a list in scala is as below

  val lst  = List(1,10,30,50)

This is the simplest way in which the list is initialized during creation itself.

val list = 50 :: 12 :: 23 :: Nil

Both are different styles of creating a list, if you are coming from java programming background then the first style is more natural. It is a programmers choice to pick one.

Prepending to List

Method1: Using the :: method

scala> val lst = List(10,200,300)
lst: List[Int] = List(10, 200, 300)

scala>  400 :: lst
res1: List[Int] = List(400, 10, 200, 300)

To the List lst a value 400 was prepended. This is fast operation completing in O(1) time. The :: method takes two arguments “head” which is a type of the list and the second argument is of type list. Hence 400 is the element and as second argument the list instance lst is passed. To understand this better, let us change the order of elements ( a common mistake) and see what happens

scala> lst :: 400
           ^
       error: value :: is not a member of Int

The compiler throws error because it expects an element first ( head) and a list instance second and not the other way around. Scala automatically infers the type of the List.

Method 2: Use +: method

scala> val lst = List(10,200,300)
lst: List[Int] = List(10, 200, 300)

scala> 23 +: lst
res4: List[Int] = List(23, 10, 200, 300)

Append to a Scala List

From a performance stand point, if you want to add items to the list use the prepend option. Appending means you are adding an element to the end of the list and it is not an efficient operation. But in case the list is small and the performance impact is negligible then the :+ method can be used.

scala> lst :+ 500
res2: List[Int] = List(10, 200, 300, 500)

The element 500 got added to the end of the list.

Head and Tail

Scala List provides convenient methods to the get the first element of the list called “head” and rest of the list without “head” called tail. That is head returns a single element of the list and tail returns a new list without the first element. There are many usecases where this is very handy.

scala> val lst = List(10,200,300)
lst: List[Int] = List(10, 200, 300)

scala> lst.head
res6: Int = 10

scala> lst.tail
res7: List[Int] = List(200, 300)

You can see the return type of tail is a List[T] , List[Int] in this case where as the head returns Int.

Pattern Matching in Scala Lists

One of the most powerful features of scala is its pattern matching capabilities using case classes. The same case class based pattern matching works very well with lists. Let us see few examples of understand this better.

val fruits = List("Grapes", "Oranges", "Mangoes", "Apples")

 // Pattern match
  fruits match {
    case firstFruit :: otherFruits =>
      println("The first day of the week is: " + firstFruit+" , "+otherFruits)
    case List() =>
      println("There don't seem to be any week days.")
  }

The above code is actually very simple if we understand what is being done in the first case condition. It is head :: tail method invocations ( refer above sections for head and tail) on the fruits list. The variable “firstFruit” holds the head of the list and the “otherFruits”, variable holds the tail ( list without the first item). The second case construct, checks whether the list is an empty one. This type of pattern based coding makes the code more compact and elegant.

You can also do case isEmpty, instead of comparing with empty List via List()

Merging Two Lists

Often it is required to combine / merge two lists into one. Scala offers : : : to do just that.

scala> val listone = List("a","b","c")
listone: List[String] = List(a, b, c)

scala> val listtwo = List("d","e","f")
listtwo: List[String] = List(d, e, f)

scala> listone ::: listtwo
res0: List[String] = List(a, b, c, d, e, f)

Do note the difference between the : : and : : : First one adds the element to the front of the list and the second syntax adds the elements of a given list to this list.

Leave a Comment