A list is constructed from cons
cells. From the R5RS list section:
The objects in the car fields of successive pairs of a list are the elements of the list. For example, a two-element list is a pair whose car is the first element and whose cdr is a pair whose car is the second element and whose cdr is the empty list. The length of a list is the number of elements, which is the same as the number of pairs.
For example, the list (a b c)
is equivalent to the following series of pairs: (a . (b . (c . ())))
And could be represented in memory by the following "nodes":
[p] --> [p] --> [p] --> null
| | |
|==> a |==> b |==> c
With each node []
containing a pointer p
to the value (it's car
), and another pointer to the next element (it's cdr
).
This allows the list to grow to an unlimited length, but requires a ref
operation to start at the front of the list and traverse k
elements in order to find the requested one. As you stated, this is O(n).
By contrast, a vector is basically an array of values which could be internally represented as an array of pointers. For example, the vector #(a b c)
might be represented as:
[p p p]
| | |
| | |==> c
| |
| |==> b
|
|==> a
Where the array []
contains a series of three pointers, and each pointer is assigned to a value in the vector. So internally you could reference the third element of the vector v
using the notation v[3]
. Since you do not need to traverse the previous elements, vector-ref
is an O(1) operation.
The main disadvantage is that vectors are of fixed size, so if you need to add more elements than the vector can hold, you have to allocate a new vector and copy the old elements to this new vector. This can potentially be an expensive operation if your application does this on a regular basis.
There are many resources online - this article on Scheme Data Structures goes into more detail and provides some examples, although it is much more focused on lists.
All that said, if your keys are (or can become) integers and you either have a fixed number of elements or can manage with a reasonable amount of vector reallocations - for example, you load the vector at startup and then perform mostly reads - a vector may be an attractive alternative to an association list.