列表由单元格cons
构成。从R 5 RS 列表部分:
列表的连续对的汽车字段中的对象是列表的元素。例如,双元素列表是一个对,其 car 是第一个元素,其 cdr 是一个对,其 car 是第二个元素,其 cdr 是空列表。列表的长度是元素的数量,与对的数量相同。
例如,该列表(a b c)
等效于以下一系列对:(a . (b . (c . ())))
并且可以通过以下“节点”在内存中表示:
[p] --> [p] --> [p] --> null
| | |
|==> a |==> b |==> c
每个节点都[]
包含一个指向p
值的指针 (it's car
),以及另一个指向下一个元素的指针 (it's cdr
)。
这允许列表增长到无限长度,但需要一个ref
操作从列表的前面开始并遍历k
元素以找到请求的元素。正如你所说,这是O(n)。
相比之下,向量基本上是一个值数组,可以在内部表示为指针数组。例如,向量#(a b c)
可能表示为:
[p p p]
| | |
| | |==> c
| |
| |==> b
|
|==> a
其中数组[]
包含一系列三个指针,每个指针都分配给向量中的一个值。v
所以在内部你可以使用符号引用向量的第三个元素v[3]
。由于您不需要遍历前面的元素,vector-ref
因此是 O(1) 操作。
主要缺点是向量的大小是固定的,因此如果您需要添加的元素多于向量可以容纳的数量,则必须分配一个新向量并将旧元素复制到这个新向量中。如果您的应用程序定期执行此操作,这可能是一项昂贵的操作。
网上有很多资源——这篇关于Scheme 数据结构的文章更详细地介绍并提供了一些示例,尽管它更侧重于列表。
综上所述,如果您的键是(或可以成为)整数,并且您具有固定数量的元素,或者可以通过合理数量的向量重新分配来管理 - 例如,您在启动时加载向量,然后主要执行读取 - 一个向量可能是关联列表的一个有吸引力的替代方案。