 As we discussed earlier, the special thing about Clojure's collection types is that they are fully persistent. This means they are immutable, but implemented in such a way as to allow for efficient creation of modified copies. Before covering the many standard library functions that create these modified copies, let's look at how the collections are structured to enable persistence. The simplest collection type is the list, which is implemented as a singly linked list. Each element of a list is stored in a node with a link to another node, such that a list is a chain of nodes. A list object references the first element node, the so-called head node, and the last element node breaks the chain by having a nil reference. Given this structure, accessing elements of the array is a big O-N operation. For example, if we want to access the fourth element of an array, we have to follow the chain from the head until we reach the fourth element. So these lists do not give good performance for random access. Because Clojure lists are immutable, we never modify the existing nodes. Consider then what we must do to create a copy of the list in which we substitute one new value. To replace the value at index 2, for example, we must create a new node for index 2, which references the existing node at index 3. But because we can't modify what the existing node at index 1 references, we must create a new node for index 1 as well to reference our new node for index 2. And then because we have a new node at index 1, we need a new node at index 0, the first node of the list. So in the best-case scenario, we're replacing the head itself and so only need one new node. But in the worst-case scenario, we're replacing the last node and therefore must replace every other node along with it. In other words, replacing an element of a list is another big O-N operation, both in terms of CPU time and memory usage. As we've already shown, Clojure uses lists at the core of its syntax, but because they don't have great performance characteristics, we typically don't use lists much for actually storing data. For that purpose, we instead mostly use Clojure's other ordered collection type, vectors. Without going into all the details, vectors are structured as a tree of nodes, with the actual elements stored in the leaf nodes. The vector object itself simply references the head node of the tree. A simple algorithm is used to traverse from the head directly down the chain to an element of a particular index. It works out that looking up an element at a particular index is a big O-log-N operation, which is much better than big O-N. And because each node stores up to 32 child nodes or elements, the lookup operations actually log base 32-N, which in practice makes lookup operations nearly as good as big O-1, constant time. Now, if we wish to create a modified copy of a vector in which we replace the element at an index, the same traversal algorithm is used to find the chain of nodes from the head down to the leaf node that contains that element. That chain is then copied, and in the copied leaf node, the element is replaced, and a new vector object is created, which points to the head of this new chain of nodes. The old vector still exists untouched, and we have a new vector that is just like the old, except for having one different element. To be sure, relative to a simple array, vectors impose overhead costs for basic operations like lookups and replacing elements, but in most cases, this overhead is a reasonable price to pay for the large advantages of having an immutable data structure. As for Clojure's hash maps, they use a very similar tree of nodes structure, but traversal for looking up a key-value pair is based on a hash of the key. We won't discuss the details here.