假设我在Elixir中有一张地图:
m = %{"a"=>1, "b"=>2, "c" => 3}
如果我致电Map.values(m)
,我保证按此顺序返回值始终为[1, 2, 3]
,而不是说[3, 1, 2]
?
这是我从文档中不清楚的一件事。经过一些初步测试后,我认为是。
答案 0 :(得分:16)
在Elixir和Erlang中实现Maps有一些令人困惑的属性。对于较小的条目值,它是一个排序的键列表,因此看起来具有您在简单实验中看到的属性。
超过一定数量的条目(我认为32),实现切换到Hash Array Mapped Trie,您看到的所有属性都消失了。在一般情况下,您不能依赖于键的顺序或映射的值。见
https://en.wikipedia.org/wiki/Hash_array_mapped_trie
解释Map的底层结构。
iex(7)> 1..33 |> Enum.reduce(%{}, fn(x, acc) -> Map.put(acc,x,x) end )
%{11 => 11, 26 => 26, 15 => 15, 20 => 20, 17 => 17, 25 => 25, 13 => 13, 8 => 8, 7 => 7, 1 => 1, 32 => 32, 3 => 3, 6 => 6, 2 => 2, 33 => 33, 10 => 10, 9 => 9, 19 => 19, 14 => 14, 5 => 5, 18 => 18, 31 => 31, 22 => 22, 29 => 29, 21 => 21, 27 => 27, 24 => 24, 30 => 30, 23 => 23, 28 => 28, 16 => 16, 4 => 4, 12 => 12}
iex(8)> Map.keys(v(7)) [11, 26, 15, 20, 17, 25, 13, 8, 7, 1, 32, 3, 6, 2, 33, 10, 9, 19, 14, 5, 18, 31, 22, 29, 21, 27, 24, 30, 23, 28, 16, 4, 12]
iex(9)> Map.values(v(7)) [11, 26, 15, 20, 17, 25, 13, 8, 7, 1, 32, 3, 6, 2, 33, 10, 9, 19, 14, 5, 18, 31, 22, 29, 21, 27, 24, 30, 23, 28, 16, 4, 12]
答案 1 :(得分:7)
与关键字列表相比,我们已经看到两个不同之处:
- 地图允许任何值作为键。
- 地图的密钥不按任何顺序排列。
虽然Elixir网站明确指出地图不遵循任何订购,但他们确实在创建后遵循特定订单(但不保留其创建顺序)。似乎地图按照他们的键按字母顺序排列(但除了IEx中的一些实验外,我没有任何支持):
map = %{c: 3, a: 1, b: 2}
map # => %{a: 1, b: 2, c: 3}
Map.keys(map) # => [:a, :b, :c]
Map.values(map) # => [1, 2, 3]
由于您询问了保留原始订单的问题,答案是否定的。
更好的选择是使用关键字列表(下面是两个元素元组的链接列表)。因此,维护其创建顺序:
kw = [c: 3, a: 1, b: 2]
kw # => [c: 3, a: 1, b: 2]
Keyword.keys(kw) # => [:c, :a, :b]
Keyword.values(kw) # => [3, 1, 2]
答案 2 :(得分:0)
让我们检查一下Erlang的代码。
Elixir使用Erlang的地图作为基础实现,它定义了一个MAP_SMALL_MAP_LIMIT宏。
erts/emulator/beam/erl_map.h-#ifdef DEBUG
erts/emulator/beam/erl_map.h:#define MAP_SMALL_MAP_LIMIT (3)
erts/emulator/beam/erl_map.h-#else
erts/emulator/beam/erl_map.h:#define MAP_SMALL_MAP_LIMIT (32)
erts/emulator/beam/erl_map.h-#endif
创建地图时,它将使用以下功能。
Eterm erts_map_from_ks_and_vs(ErtsHeapFactory *factory, Eterm *ks0, Eterm *vs0, Uint n)
{
if (n < MAP_SMALL_MAP_LIMIT) {
Eterm *ks, *vs, *hp;
flatmap_t *mp;
Eterm keys;
hp = erts_produce_heap(factory, 3 + 1 + (2 * n), 0);
keys = make_tuple(hp);
*hp++ = make_arityval(n);
ks = hp;
hp += n;
mp = (flatmap_t*)hp;
hp += MAP_HEADER_FLATMAP_SZ;
vs = hp;
mp->thing_word = MAP_HEADER_FLATMAP;
mp->size = n;
mp->keys = keys;
sys_memcpy(ks, ks0, n * sizeof(Eterm));
sys_memcpy(vs, vs0, n * sizeof(Eterm));
erts_validate_and_sort_flatmap(mp);
return make_flatmap(mp);
} else {
return erts_hashmap_from_ks_and_vs(factory, ks0, vs0, n);
}
return THE_NON_VALUE;
}
请注意,当地图的尺寸小于MAP_SMALL_MAP_LIMIT
时,它会调用erts_validate_and_sort_flatmap
,它会使用冒泡排序来对地图进行排序。
否则,它将使用hashmap。