33

This is a followup question to No Scala mutable list

I want to use a mutable list in Scala. I can chose from

Which is nice, but what is the "standard", recommended, idiomatic scala way? I just want to use a list that I can add things to on the back.

In my case, I am using a HashMap, where the "lists" (I am meaning it in general sense) will be on value side. Then, I am reading something from a file and for every line, I want to find the right list in the hashmap and append the value to the list.

Cory Klein
  • 51,188
  • 43
  • 183
  • 243
Karel Bílek
  • 36,467
  • 31
  • 94
  • 149
  • 2
    You should probably expand on how you'll be using it - because in a general sense, the idiomatic Scala way is not to use mutable lists at all (instead use folds or recursion with immutable lists). – Andrzej Doyle Jun 15 '12 at 10:57
  • I think your lists can be immutable. You can simply prepend to an immutable list and update your HashMap entry to that newly created list. – ziggystar Jun 15 '12 at 12:31
  • Well, in that case, I will have to change the hashmap instead of the lists. Right now, I don't change the hashmap, but I change the lists. – Karel Bílek Jun 15 '12 at 12:40
  • Also see http://stackoverflow.com/q/5446744/515380 for an explanation of the differences between ListBuffer and MutableList. – Mike Jun 16 '12 at 10:05

4 Answers4

36

Depends what you need.

DoubleLinkedList is a linked list which allows you to traverse back-and-forth through the list of nodes. Use its prev and next references to go to the previous or the next node, respectively.

LinkedList is a singly linked list, so there are not prev pointers - if you only traverse to the next element of the list all the time, this is what you need.

EDIT: Note that the two above are meant to be used internally as building blocks for more complicated list structures like MutableLists which support efficient append, and mutable.Queues.

The two collections above both have linear-time append operations.

ListBuffer is a buffer class. Although it is backed by a singly linked list data structure, it does not expose the next pointer to the client, so you can only traverse it using iterators and the foreach. Its main use is, however, as a buffer and an immutable list builder - you append elements to it via +=, and when you call result, you very efficiently get back a functional immutable.List. Unlike mutable and immutable lists, both append and prepend operations are constant-time - you can append at the end via += very efficiently.

MutableList is used internally, you usually do not use it unless you plan to implement a custom collection class based on the singly linked list data structure. Mutable queues, for example, inherit this class. MutableList class also has an efficient constant-time append operation, because it maintains a reference to the last node in the list.

axel22
  • 32,045
  • 9
  • 125
  • 137
  • The linked lists really have linear append? Why? Both single and double linked lists can support append in constant time, at least in fully mutable world. – svick Jun 15 '12 at 21:34
  • Yes, they do - see here: https://github.com/scala/scala/blob/v2.9.2/src/library/scala/collection/mutable/LinkedListLike.scala#L1. Why? The design decision was to make each node a linked list in itself - otherwise you would need a wrapper around the starting and the ending node references, which is exactly what the `MutableList` is. – axel22 Jun 16 '12 at 00:42
  • I don't see an answer in docs for "which list is best at removing elements" e.g. if I plan to build a list then remove tons of items one at a time? – Hamy Apr 25 '13 at 22:40
  • I found this page because I also want to know the standard, recommended, idiomatic way. From this detailed explanation, it is my impression that the answer is `ListBuffer`. – Jim Pivarski Sep 10 '13 at 20:15
  • The answer to the question of what's best for adding to the end is `ListBuffer`. Not only is it pretty efficient, it can also eventually yield an immutable `List` object, which is pretty idiomatic. The other lists are not so commonly used. – axel22 Sep 10 '13 at 20:35
  • linear append... why would anyone use that?? Scala needs to ditch the ivory tower mindset and get more pragmatic if it wants to be taken seriously. – Eric Hartford Jan 13 '14 at 06:09
  • 2
    An ivory tower is a rather strong statement to make. There is indeed a use-case for the class in the standard library called `LinkedList`, and that is to build higher abstractions. A `LinkedList` in Scala is basically a linked list node and as such is a building block for classes like doubly linked `Queue`s, `MutableList`s and circular lists, i.e. rings. Storing length, last node and similar in every list node is nonsense. **Note**: `LinkedList` was not meant to be used in typical client code directly -- `MutableList`, `Queue`, `immutable.ListBuffer`s and `immutable.List`s exist for that. – axel22 Jan 13 '14 at 14:15
22

The documentation's Concrete Mutable Collection Classes page (or the one for 2.12) has an overview of mutable list classes, including explanations on when to use which one.

Daniel Werner
  • 1,350
  • 16
  • 26
11

If you want to append items you shouldn't use a List at all. Lists are good when you want to prepend items. Use ArrayBuffer instead.

drexin
  • 24,225
  • 4
  • 67
  • 81
  • 6
    I don't agree. If you don't need random access, `ListBuffer` has better performance characteristics than `ArrayBuffer`, which needs to resize from time to time. Sure, append operations may run in *amortized* constant time or something, which is not bad, but I don't see any advantage of `ArrayBuffer` here. – rolve Nov 08 '12 at 10:11
  • 5
    @rolve Amortized constant time is often faster in the end, because the constants involved for allocating a new object on the JVM (and dealing with the associated GC pressure) are quite high, while the resize operations happen very rarely (on average each item is copied twice). To say nothing of the cache locality benefits of ArrayBuffer, which can be massive on modern CPUs. – John Colanduoni Aug 25 '15 at 03:53
  • 3
    @JohnColanduoni With almost 3 years passed since my comment, I agree that `ArrayBuffer` is probably the better choice. Anyway, actual performance measurements should be used as a basis for this discussion. :) – rolve Aug 30 '15 at 13:45
2

I just want to use a list that I can add things to on the back.

Then choose something that implements Growable. I personally suggest one of the Buffer implementations.

I stay away from LinkedList and DoubleLinkedList, as they are present mainly as underlying implementation of other collections, but have quite a few bugs up to Scala 2.9.x. Starting with Scala 2.10.0, I expect the various bug fixes have brought them up to standard. Still, they lack some methods people expect, such as +=, which you'll find on collections based on them.

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681