Why is appending to a list bad?
The key is that x :: somelist
does not mutate somelist
, but instead creates a new list, which contains x followed by all elements of somelist
. This can be done in O(1) time because you only need to set somelist
as the successor of x
in the newly created, singly linked list.
If doubly linked lists were used instead, x
would also have to be set as the predecessor of somelist
's head, which would modify somelist
. So if we want to be able to do ::
in O(1) without modifying the original list, we can only use singly linked lists.
Regarding the second question: You can use :::
to concatenate a single-element list to the end of your list. This is an O(n) operation.
List(1,2,3) ::: List(4)
Other answers have given good explanations for this phenomenon. If you are appending many items to a list in a subroutine, or if you are creating a list by appending elements, a functional idiom is to build up the list in reverse order, cons'ing the items on the front of the list, then reverse it at the end. This gives you O(n) performance instead of O(n²).
Since the question was just updated, it's worth noting that things have changed here.
In today's Scala, you can simply use xs :+ x
to append an item at the end of any sequential collection. (There is also x +: xs
to prepend. The mnemonic for many of Scala's 2.8+ collection operations is that the colon goes next to the collection.)
This will be O(n) with the default linked implementation of List
or Seq
, but if you use Vector
or IndexedSeq
, this will be effectively constant time. Scala's Vector
is probably Scala's most useful list-like collection—unlike Java's Vector
which is mostly useless these days.
If you are working in Scala 2.8 or higher, the collections introduction is an absolute must read.