What is the difference between Scala's MutableList
and ListBuffer
classes in scala.collection.mutable
? When would you use one vs the other?
My use case is having a linear sequence where I can efficiently remove the first element, prepend, and append. What's the best structure for this?
The ListBuffer Scaladoc states that a ListBuffer is “a Buffer implementation backed by a list. It provides constant time prepend and append.
Scala provides a data structure, the ListBuffer, which is more efficient than List while adding/removing elements in a list. It provides methods to prepend, append elements to a list.
A MutableList consists of a single linked list together with a pointer that refers to the terminal empty node of that list. This makes list append a constant time operation because it avoids having to traverse the list in search for its terminal node. MutableList is currently the standard implementation of mutable.
A little explanation on how they work.
ListBuffer
uses internally Nil
and ::
to build an immutable List
and allows constant-time removal of the first and last elements. To do so, it keeps a pointer on the first and last element of the list, and is actually allowed to change the head and tail of the (otherwise immutable) ::
class (nice trick allowed by the private[scala] var
members of ::
). Its toList
method returns the normal immutable List
in constant time as well, as it can directly return the structure maintained internally. It is also the default builder for immutable List
s (and thus can indeed be reasonably expected to have constant-time append). If you call toList
and then again append an element to the buffer, it takes linear time with respect to the current number of elements in the buffer to recreate a new structure, as it must not mutate the exported list any more.
MutableList
works internally with LinkedList
instead, an (openly, not like ::
) mutable linked list implementation which knows of its element and successor (like ::
). MutableList
also keeps pointers to the first and last element, but toList
returns in linear time, as the resulting List
is constructed from the LinkedList
. Thus, it doesn't need to reinitialize the buffer after a List
has been exported.
Given your requirements, I'd say ListBuffer
and MutableList
are equivalent. If you want to export their internal list at some point, then ask yourself where you want the overhead: when you export the list, and then no overhead if you go on mutating buffer (then go for MutableList
), or only if you mutable the buffer again, and none at export time (then go for ListBuffer
).
My guess is that in the 2.8 collection overhaul, MutableList
predated ListBuffer
and the whole Builder
system. Actually, MutableList
is predominantly useful from within the collection.mutable
package: it has a private[mutable] def toLinkedList
method which returns in constant time, and can thus efficiently be used as a delegated builder for all structures that maintain a LinkedList
internally.
So I'd also recommend ListBuffer
, as it may also get attention and optimization in the future than “purely mutable” structures like MutableList
and LinkedList
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With