Elixir映射 #

一、映射基础 #

1.1 映射定义 #

映射是键值对的集合,键可以是任意类型:

elixir
iex(1)> %{:name => "Alice", :age => 30}
%{age: 30, name: "Alice"}

iex(2)> %{"name" => "Bob", "age" => 25}
%{"age" => 25, "name" => "Bob"}

iex(3)> %{1 => "one", 2 => "two"}
%{1 => "one", 2 => "two"}

1.2 原子键简写 #

当键是原子时,可以使用简写语法:

elixir
iex(1)> %{name: "Alice", age: 30}
%{age: 30, name: "Alice"}

iex(2)> %{:name => "Alice", :age => 30}
%{age: 30, name: "Alice"}

1.3 空映射 #

elixir
iex(1)> %{}
%{}

1.4 映射大小 #

elixir
iex(1)> map_size(%{a: 1, b: 2, c: 3})
3

二、访问与更新 #

2.1 访问值 #

点号语法(仅原子键) #

elixir
iex(1)> user = %{name: "Alice", age: 30}
%{age: 30, name: "Alice"}

iex(2)> user.name
"Alice"

iex(3)> user.age
30

方括号语法 #

elixir
iex(1)> user = %{name: "Alice", age: 30}
%{age: 30, name: "Alice"}

iex(2)> user[:name]
"Alice"

iex(3)> user[:unknown]
nil

Map.get函数 #

elixir
iex(1)> user = %{name: "Alice", age: 30}
%{age: 30, name: "Alice"}

iex(2)> Map.get(user, :name)
"Alice"

iex(3)> Map.get(user, :unknown)
nil

iex(4)> Map.get(user, :unknown, "default")
"default"

2.2 更新值 #

更新语法 #

elixir
iex(1)> user = %{name: "Alice", age: 30}
%{age: 30, name: "Alice"}

iex(2)> %{user | age: 31}
%{age: 31, name: "Alice"}

iex(3)> %{user | name: "Bob", age: 25}
%{age: 25, name: "Bob"}

注意:只能更新已存在的键。

Map.put函数 #

elixir
iex(1)> user = %{name: "Alice"}
%{name: "Alice"}

iex(2)> Map.put(user, :age, 30)
%{age: 30, name: "Alice"}

iex(3)> Map.put(user, :name, "Bob")
%{name: "Bob"}

2.3 添加键 #

elixir
iex(1)> user = %{name: "Alice"}
%{name: "Alice"}

iex(2)> Map.put(user, :age, 30)
%{age: 30, name: "Alice"}

iex(3)> Map.put_new(user, :age, 30)
%{age: 30, name: "Alice"}

iex(4)> Map.put_new(user, :name, "Bob")
%{name: "Alice"}

2.4 删除键 #

elixir
iex(1)> user = %{name: "Alice", age: 30}
%{age: 30, name: "Alice"}

iex(2)> Map.delete(user, :age)
%{name: "Alice"}

三、Map模块函数 #

3.1 查询 #

elixir
iex(1)> user = %{name: "Alice", age: 30}
%{age: 30, name: "Alice"}

iex(2)> Map.has_key?(user, :name)
true

iex(3)> Map.has_key?(user, :email)
false

iex(4)> Map.keys(user)
[:age, :name]

iex(5)> Map.values(user)
[30, "Alice"]

3.2 合并 #

elixir
iex(1)> Map.merge(%{a: 1}, %{b: 2})
%{a: 1, b: 2}

iex(2)> Map.merge(%{a: 1, b: 1}, %{b: 2, c: 3})
%{a: 1, b: 2, c: 3}

3.3 取值与弹出 #

elixir
iex(1)> user = %{name: "Alice", age: 30}
%{age: 30, name: "Alice"}

iex(2)> Map.fetch(user, :name)
{:ok, "Alice"}

iex(3)> Map.fetch(user, :unknown)
:error

iex(4)> Map.fetch!(user, :name)
"Alice"

iex(5)> Map.pop(user, :name)
{"Alice", %{age: 30}}

iex(6)> Map.pop(user, :unknown, nil)
{nil, %{age: 30, name: "Alice"}}

3.4 转换 #

elixir
iex(1)> Map.to_list(%{a: 1, b: 2})
[a: 1, b: 2]

iex(2)> Enum.to_list(%{a: 1, b: 2})
[a: 1, b: 2]

iex(3)> Map.from_struct(%{__struct__: User, name: "Alice"})
%{name: "Alice"}

3.5 遍历 #

elixir
iex(1)> user = %{name: "Alice", age: 30}
%{age: 30, name: "Alice"}

iex(2)> Enum.map(user, fn {k, v} -> {k, String.upcase(to_string(v))} end)
[age: "30", name: "ALICE"]

iex(3)> for {key, value} <- user, do: {key, value}
[age: 30, name: "Alice"]

四、模式匹配 #

4.1 基本匹配 #

elixir
iex(1)> %{name: name, age: age} = %{name: "Alice", age: 30}
%{age: 30, name: "Alice"}

iex(2)> name
"Alice"

iex(3)> age
30

4.2 部分匹配 #

映射匹配只需要匹配存在的键:

elixir
iex(1)> %{name: name} = %{name: "Alice", age: 30, city: "NYC"}
%{age: 30, city: "NYC", name: "Alice"}

iex(2)> name
"Alice"

4.3 嵌套匹配 #

elixir
iex(1)> %{address: %{city: city}} = %{name: "Alice", address: %{city: "NYC", zip: "10001"}}
%{address: %{city: "NYC", zip: "10001"}, name: "Alice"}

iex(2)> city
"NYC"

4.4 动态键匹配 #

elixir
iex(1)> %{key => value} = %{name: "Alice", age: 30}
%{age: 30, name: "Alice"}

iex(2)> key
:age

iex(3)> value
30

4.5 函数参数匹配 #

elixir
defmodule User do
  def greet(%{name: name, age: age}) when age >= 18 do
    "Hello, #{name}! You are an adult."
  end

  def greet(%{name: name}) do
    "Hi, #{name}!"
  end
end

五、嵌套映射 #

5.1 定义嵌套映射 #

elixir
iex(1)> user = %{
...>   name: "Alice",
...>   address: %{
...>     city: "NYC",
...>     country: "USA"
...>   },
...>   contacts: %{
...>     email: "alice@example.com",
...>     phone: "123-456-7890"
...>   }
...> }
%{address: %{city: "NYC", country: "USA"}, contacts: %{email: "alice@example.com", phone: "123-456-7890"}, name: "Alice"}

5.2 访问嵌套值 #

elixir
iex(1)> user.address.city
"NYC"

iex(2)> user.contacts.email
"alice@example.com"

5.3 更新嵌套值 #

elixir
iex(1)> put_in(user.address.city, "LA")
%{address: %{city: "LA", country: "USA"}, ...}

iex(2)> update_in(user.contacts.email, &String.upcase/1)
%{contacts: %{email: "ALICE@EXAMPLE.COM", ...}, ...}

5.4 get_in/put_in/update_in #

elixir
iex(1)> get_in(user, [:address, :city])
"NYC"

iex(2)> put_in(user, [:address, :city], "LA")
%{address: %{city: "LA", country: "USA"}, ...}

iex(3)> update_in(user, [:address, :city], &String.upcase/1)
%{address: %{city: "NYC", country: "USA"}, ...}

5.5 pop_in #

elixir
iex(1)> pop_in(user, [:address, :city])
{"NYC", %{address: %{country: "USA"}, ...}}

六、映射与关键字列表 #

6.1 主要区别 #

特性 映射 关键字列表
键类型 任意 原子
键重复 不允许 允许
顺序 无序 有序
模式匹配 支持 有限支持
性能 O(log n) O(n)

6.2 选择建议 #

  • 使用映射:需要任意键类型、唯一键、快速查找
  • 使用关键字列表:函数选项、有序键值对
elixir
def configure(opts) do
  default = %{host: "localhost", port: 8080}
  Enum.reduce(opts, default, fn {key, value}, acc ->
    Map.put(acc, key, value)
  end)
end

configure(port: 3000, ssl: true)

七、映射推导式 #

7.1 创建映射 #

elixir
iex(1)> for x <- 1..3, into: %{}, do: {x, x * 2}
%{1 => 2, 2 => 4, 3 => 6}

7.2 过滤转换 #

elixir
iex(1)> data = %{a: 1, b: 2, c: 3, d: 4}
%{a: 1, b: 2, c: 3, d: 4}

iex(2)> for {k, v} <- data, v > 2, into: %{}, do: {k, v * 2}
%{c: 6, d: 8}

八、映射与JSON #

8.1 编码 #

elixir
iex(1)> Jason.encode(%{name: "Alice", age: 30})
{:ok, "{\"age\":30,\"name\":\"Alice\"}"}

iex(2)> Jason.encode!(%{name: "Alice", age: 30})
"{\"age\":30,\"name\":\"Alice\"}"

8.2 解码 #

elixir
iex(1)> Jason.decode("{\"name\":\"Alice\",\"age\":30}")
{:ok, %{"age" => 30, "name" => "Alice"}}

iex(2)> Jason.decode!("{\"name\":\"Alice\",\"age\":30}")
%{"age" => 30, "name" => "Alice"}

iex(3)> Jason.decode!("{\"name\":\"Alice\",\"age\":30}", keys: :atoms)
%{age: 30, name: "Alice"}

九、性能考虑 #

9.1 小映射 #

小映射(<= 32个键)使用紧凑存储,性能接近列表。

9.2 大映射 #

大映射使用HAMT(Hash Array Mapped Trie),查找复杂度O(log n)。

9.3 最佳实践 #

elixir
user = %{name: "Alice", age: 30}

user.name

user[:name]

Map.get(user, :name, "default")

十、总结 #

本章学习了:

操作 示例
创建 %{a: 1, b: 2}
访问 map.key, map[:key]
更新 %{map | key: value}
添加 Map.put(map, :key, value)
删除 Map.delete(map, :key)
合并 Map.merge(map1, map2)
嵌套 put_in(map[:a][:b], value)

准备好学习关键字列表了吗?让我们进入下一章。

最后更新:2026-03-27