F Sharp Programming/Mutable Collections

The .NET BCL comes with its own suite of mutable collections which are found in the System.Collections.Generic namespace. These built-in collections are very similar to their immutable counterparts in F#.

Class
The  class represents a strongly typed list of objects that can be accessed by index. Conceptually, this makes the  class similar to arrays. However, unlike arrays, s can be resized and don't need to have their size specified on declaration.

.NET lists are created using the  keyword and calling the list's constructor as follows:

It's easy to tell that .NET lists are mutable because their  methods return   rather than returning another list.

Underlying Implementation
Behind the scenes, the  class is just a fancy wrapper for an array. When a  is constructed, it creates an 4-element array in memory. Adding the first 4 items is an O(1) operation. However, as soon as the 5th element needs to be added, the list doubles the size of the internal array, which means it has to reallocate new memory and copy elements in the existing list; this is a O(n) operation, where n is the number of items in the list.

The  property returns the number of items currently held in the collection, the   collection returns the size of the underlying array. This code sample demonstrates how the underlying array is resized:

l.Count: 0, l.Capacity: 0 Items: --- l.Count: 1, l.Capacity: 4 Items: l.[0]: monkey --- l.Count: 3, l.Capacity: 4 Items: l.[0]: monkey l.[1]: kitty l.[2]: bunny --- l.Count: 6, l.Capacity: 8 Items: l.[0]: monkey l.[1]: kitty l.[2]: bunny l.[3]: doggy l.[4]: octopussy l.[5]: ducky --- Removing entry for "doggy"

l.Count: 5, l.Capacity: 8 Items: l.[0]: monkey l.[1]: kitty l.[2]: bunny l.[3]: octopussy l.[4]: ducky --- Removing item at index 3

l.Count: 4, l.Capacity: 8 Items: l.[0]: monkey l.[1]: kitty l.[2]: bunny l.[3]: ducky ---

If you know the maximum size of the list beforehand, it is possible to avoid the performance hit by calling the  constructor instead. The following sample demonstrates how to add 1000 items to a list without resizing the internal array:

Class
A  represented a doubly-linked sequence of nodes which allows efficient O(1) inserts and removal, supports forward and backward traversal, but its implementation prevents efficient random access. Linked lists have a few valuable methods:

Note that these methods return a, not a new. Adding nodes actually mutates the  object:

The  and   classes are special cases of a linked list (they can be thought of as linked lists with restrictions on where you can add and remove items).

Class
A  only allows programmers prepend/push and remove/pop items from the front of a list, which makes it a last in, first out (LIFO) data structure. The  class can be thought of as a mutable version of the F# list.

A stack of coins could be represented with this data structure. If we stacked coins one on top another, the first coin in the stack is at the bottom of the stack, and the last coin in the stack appears at the top. We remove coins from top to bottom, so the last coin added to the stack is the first one removed.



Class
A  only allows programmers to append/enqueue to the rear of a list and remove/dequeue from the front of a list, which makes it a first in, first out (FIFO) data structure.

A line of people might be represented by a queue: people add themselves to the rear of the line, and are removed from the front. The first person to stand in line is the first person to be served.



, and Classes
The  and   classes are mutable analogs of the F# set and map data structures and contain many of the same functions.

Using the HashSet<'T>

nums_1to10 (before): 1 2 3 4 5 6 7 8 9 10 nums_5to15 (before): 5 6 7 8 9 10 11 12 13 14 15 nums_1to10 (after): 5 6 7 8 9 10 nums_5to15 (after): 5 6 7 8 9 10 11 12 13 14 15

Using the Dictionary<'TKey, 'TValue>

Differences Between .NET BCL and F# Collections
The major difference between the collections built into the .NET BCL and their F# analogs is, of course, mutability. The mutable nature of BCL collections dramatically affects their implementation and time-complexity:


 * * These classes are built on top of internal arrays. They may take a performance hit as the internal arrays are periodically resized when adding items.


 * Note: the Big-O notation above refers to the time-complexity of the insert/remove/retrieve operations relative to the number of items in the data structure, not the relative amount of time required to evaluate the operations relative to other data structures. For example, accessing arrays by index vs. accessing dictionaries by key have the same time complexity, O(1), but the operations do not necessarily occur in the same amount of time.