在地图上操作时,Elixir中是否保留了键和值的排序?

Non*_*ona 11 elixir

假设我在Elixir有一张地图:

m = %{"a"=>1, "b"=>2, "c" => 3}
Run Code Online (Sandbox Code Playgroud)

如果我打电话Map.values(m),我保证返回值总是按[1, 2, 3]顺序而不是说,[3, 1, 2]

这是我从文档中不清楚的一件事.经过一些初步测试,我认为是.

Fre*_*Dog 21

在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]
Run Code Online (Sandbox Code Playgroud)


She*_*yar 8

来自Elixir网站:

与关键字列表相比,我们已经看到两个不同之处:

  • 地图允许任何值作为键.
  • 地图的密钥不遵循任何订购.

虽然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]
Run Code Online (Sandbox Code Playgroud)

由于您询问了保留原始订单的问题,答案是否定的.


更好的选择:关键字列表

更好的选择是使用关键字列表(下面是两个元素元组的链接列表).因此,维护其创建顺序:

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]
Run Code Online (Sandbox Code Playgroud)

  • 不,这是完全错误的,你所看到的只是为少数键实现地图的方式.在某个点之下,它们只是排序的关键字列表,在它们之上是一个特里哈希. (3认同)
  • 永远不要依赖 Map 的顺序,它是一个随时可能更改的实现细节。 (2认同)
  • @Overbryd 这是为什么_不_依赖排序的示例: `Enum.map(1..32, &{&1, String.duplicate("*", &1)}) |> Enum.into(%{})`与`Enum.map(1..33, &{&1, String.duplicate("*", &1)}) |> Enum.into(%{})`。在 iex 中运行它们,您将看到第一个示例保留了顺序,而第二个示例则没有。 (2认同)