Clojure provides a number of concrete data structures, each of which satisfy various abstractions as appropriate. We’ve already been working with these concrete implementations quite a lot—indeed, as we’d hope, their behavior and semantics are defined for the most part by the abstractions (or parts of abstractions) they participate in.
Here, we’ll rapidly move through some of the implementation details that separate each of the concrete data structure types, most of which have to do with their construction.
Lists are the simplest collection type in Clojure. Their primary and most typical purpose is to serve as a representation for calls in Clojure code, as we explained in Expressions, Operators, Syntax, and Precedence; as such, you’ll use them far more as literals in your source files than you ever will at runtime in your programs.
Clojure lists are singly linked, but are only efficiently accessed
or “modified” at their head, using
conj to push a new head value on, or
pop or the sequence operator
rest to obtain a reference to the sublist without the prior
head value. Because they are linked lists, they do not support efficient
random access; thus,
nth on a list will run in linear time (as opposed to constant
time when used with vectors, arrays, and so on), and
get does not support lists at all because
doing so would not align with
objective of sublinear efficiency.
It is worth noting that lists are their own sequences; therefore,
seq of a list will ...