Erlang映射 #

一、映射概述 #

映射(Map)是Erlang/OTP 17引入的数据类型,用于存储键值对集合。

1.1 映射的特点 #

  • 键可以是任意Erlang项
  • 键值对无序存储
  • 高效的查找、插入、删除
  • 适合表示结构化数据

二、映射定义 #

2.1 基本定义 #

erlang
-module(map_def).
-export([demo/0]).

demo() ->
    Empty = #{},
    Simple = #{a => 1, b => 2, c => 3},
    Mixed = #{1 => "one", "two" => 2, atom => three},
    Nested = #{user => #{name => "Alice", age => 30}},
    
    io:format("Empty: ~p~n", [Empty]),
    io:format("Simple: ~p~n", [Simple]),
    io:format("Mixed: ~p~n", [Mixed]),
    io:format("Nested: ~p~n", [Nested]),
    ok.

2.2 从列表创建 #

erlang
-module(map_from_list).
-export([demo/0]).

demo() ->
    List = [{a, 1}, {b, 2}, {c, 3}],
    Map = maps:from_list(List),
    io:format("List: ~p~n", [List]),
    io:format("Map: ~p~n", [Map]),
    ok.

三、映射操作 #

3.1 获取值 #

erlang
-module(map_get).
-export([demo/0]).

demo() ->
    Map = #{a => 1, b => 2, c => 3},
    
    A = maps:get(a, Map),
    B = Map#{b},
    Default = maps:get(d, Map, default_value),
    
    io:format("Map: ~p~n", [Map]),
    io:format("a: ~p~n", [A]),
    io:format("b: ~p~n", [B]),
    io:format("d (default): ~p~n", [Default]),
    ok.

3.2 添加/更新值 #

erlang
-module(map_update).
-export([demo/0]).

demo() ->
    Map1 = #{a => 1, b => 2},
    
    Map2 = Map1#{c => 3},
    Map3 = Map2#{a => 10},
    Map4 = Map3#{d := 4},
    
    io:format("Map1: ~p~n", [Map1]),
    io:format("Map2 (add c): ~p~n", [Map2]),
    io:format("Map3 (update a): ~p~n", [Map3]),
    io:format("Map4 (:= syntax): ~p~n", [Map4]),
    ok.

=>:= 的区别:

  • =>:添加新键或更新已有键
  • :=:仅更新已有键(键必须存在)

3.3 删除键 #

erlang
-module(map_remove).
-export([demo/0]).

demo() ->
    Map = #{a => 1, b => 2, c => 3},
    
    Map2 = maps:remove(b, Map),
    
    io:format("Original: ~p~n", [Map]),
    io:format("After remove: ~p~n", [Map2]),
    ok.

3.4 检查键 #

erlang
-module(map_check).
-export([demo/0]).

demo() ->
    Map = #{a => 1, b => 2},
    
    HasA = maps:is_key(a, Map),
    HasC = maps:is_key(c, Map),
    
    io:format("Map: ~p~n", [Map]),
    io:format("has a: ~p~n", [HasA]),
    io:format("has c: ~p~n", [HasC]),
    ok.

3.5 获取键列表 #

erlang
-module(map_keys).
-export([demo/0]).

demo() ->
    Map = #{a => 1, b => 2, c => 3},
    
    Keys = maps:keys(Map),
    Values = maps:values(Map),
    ToList = maps:to_list(Map),
    
    io:format("Map: ~p~n", [Map]),
    io:format("Keys: ~p~n", [Keys]),
    io:format("Values: ~p~n", [Values]),
    io:format("To list: ~p~n", [ToList]),
    ok.

四、maps模块函数 #

4.1 常用函数 #

函数 说明
maps:new/0 创建空映射
maps:from_list/1 从列表创建
maps:to_list/1 转换为列表
maps:get/2 获取值
maps:get/3 获取值(带默认值)
maps:put/3 添加/更新键值对
maps:remove/2 删除键
maps:is_key/2 检查键是否存在
maps:keys/1 获取所有键
maps:values/1 获取所有值
maps:size/1 获取大小
maps:merge/2 合并映射
maps:map/2 映射函数
maps:fold/3 折叠函数
maps:filter/2 过滤函数
maps:without/2 排除键

4.2 合并映射 #

erlang
-module(map_merge).
-export([demo/0]).

demo() ->
    Map1 = #{a => 1, b => 2},
    Map2 = #{b => 20, c => 3},
    
    Merged = maps:merge(Map1, Map2),
    
    io:format("Map1: ~p~n", [Map1]),
    io:format("Map2: ~p~n", [Map2]),
    io:format("Merged: ~p~n", [Merged]),
    ok.

4.3 映射函数 #

erlang
-module(map_map).
-export([demo/0]).

demo() ->
    Map = #{a => 1, b => 2, c => 3},
    
    Doubled = maps:map(fun(_K, V) -> V * 2 end, Map),
    
    io:format("Original: ~p~n", [Map]),
    io:format("Doubled: ~p~n", [Doubled]),
    ok.

4.4 折叠函数 #

erlang
-module(map_fold).
-export([demo/0]).

demo() ->
    Map = #{a => 1, b => 2, c => 3},
    
    Sum = maps:fold(fun(_K, V, Acc) -> V + Acc end, 0, Map),
    Keys = maps:fold(fun(K, _V, Acc) -> [K | Acc] end, [], Map),
    
    io:format("Map: ~p~n", [Map]),
    io:format("Sum: ~p~n", [Sum]),
    io:format("Keys: ~p~n", [Keys]),
    ok.

4.5 过滤函数 #

erlang
-module(map_filter).
-export([demo/0]).

demo() ->
    Map = #{a => 1, b => 2, c => 3, d => 4, e => 5},
    
    Evens = maps:filter(fun(_K, V) -> V rem 2 =:= 0 end, Map),
    
    io:format("Original: ~p~n", [Map]),
    io:format("Evens: ~p~n", [Evens]),
    ok.

五、映射模式匹配 #

5.1 基本匹配 #

erlang
-module(map_pattern).
-export([demo/0]).

demo() ->
    #{a := A, b := B} = #{a => 1, b => 2, c => 3},
    io:format("A=~p, B=~p~n", [A, B]),
    ok.

5.2 函数参数匹配 #

erlang
-module(map_func_match).
-export([greet/1]).

greet(#{name := Name, age := Age}) ->
    io:format("Hello ~s, you are ~p years old~n", [Name, Age]);
greet(#{name := Name}) ->
    io:format("Hello ~s~n", [Name]);
greet(_) ->
    io:format("Hello stranger~n").

5.3 匹配特定键 #

erlang
-module(match_specific_key).
-export([handle/1]).

handle(#{type := user, name := Name}) ->
    {user, Name};
handle(#{type := admin, name := Name, level := Level}) ->
    {admin, Name, Level};
handle(#{type := Type}) ->
    {unknown, Type}.

5.4 匹配嵌套映射 #

erlang
-module(nested_map_match).
-export([demo/0]).

demo() ->
    User = #{
        name => "Alice",
        address => #{
            city => "Beijing",
            country => "China"
        }
    },
    
    #{name := Name, address := #{city := City}} = User,
    io:format("Name: ~p~n", [Name]),
    io:format("City: ~p~n", [City]),
    ok.

六、实际应用 #

6.1 配置管理 #

erlang
-module(config_map).
-export([get/2, set/3, default/0]).

default() ->
    #{
        host => "localhost",
        port => 8080,
        timeout => 5000,
        debug => false
    }.

get(Key, Config) ->
    maps:get(Key, Config).

set(Key, Value, Config) ->
    Config#{Key => Value}.

6.2 用户数据 #

erlang
-module(user_map).
-export([new/3, update_age/2, to_list/1]).

new(Name, Age, Email) ->
    #{
        name => Name,
        age => Age,
        email => Email,
        created_at => erlang:timestamp()
    }.

update_age(NewAge, User) ->
    User#{age := NewAge}.

to_list(User) ->
    maps:to_list(User).

6.3 HTTP请求 #

erlang
-module(http_request).
-export([new/2, add_header/3, set_body/2]).

new(Method, Url) ->
    #{
        method => Method,
        url => Url,
        headers => #{},
        body => <<>>
    }.

add_header(Name, Value, Request) ->
    Headers = maps:get(headers, Request),
    Request#{headers := Headers#{Name => Value}}.

set_body(Body, Request) ->
    Request#{body := Body}.

6.4 缓存系统 #

erlang
-module(cache_map).
-export([new/0, get/2, put/3, delete/2, size/1]).

new() -> #{}.

get(Key, Cache) ->
    case maps:find(Key, Cache) of
        {ok, Value} -> {ok, Value};
        error -> not_found
    end.

put(Key, Value, Cache) ->
    Cache#{Key => Value}.

delete(Key, Cache) ->
    maps:remove(Key, Cache).

size(Cache) ->
    maps:size(Cache).

七、映射与记录比较 #

7.1 使用映射 #

erlang
-module(map_vs_record).
-export([map_example/0, record_example/0]).

-record(person, {name, age}).

map_example() ->
    Person = #{name => "Alice", age => 30},
    Name = maps:get(name, Person),
    Age = maps:get(age, Person),
    {Name, Age}.

record_example() ->
    Person = #person{name = "Alice", age = 30},
    Name = Person#person.name,
    Age = Person#person.age,
    {Name, Age}.

7.2 比较表 #

特性 映射 记录
动态键 支持 不支持
模式匹配 部分支持 完全支持
编译时检查
内存效率 较低 较高
适用场景 动态数据 固定结构

八、性能考虑 #

8.1 小映射优化 #

erlang
-module(map_perf).
-export([demo/0]).

demo() ->
    Small = #{a => 1, b => 2},
    Large = maps:from_list([{X, X} || X <- lists:seq(1, 100)]),
    
    io:format("Small map size: ~p~n", [maps:size(Small)]),
    io:format("Large map size: ~p~n", [maps:size(Large)]),
    ok.

8.2 更新操作 #

erlang
-module(map_update_perf).
-export([demo/0]).

demo() ->
    Map = #{a => 1},
    
    Map2 = Map#{b => 2},
    
    Map3 = Map2#{a := 10},
    
    io:format("Map: ~p~n", [Map]),
    io:format("Map2: ~p~n", [Map2]),
    io:format("Map3: ~p~n", [Map3]),
    ok.

九、实用函数 #

9.1 深度合并 #

erlang
-module(deep_merge).
-export([merge/2]).

merge(Map1, Map2) when is_map(Map1), is_map(Map2) ->
    maps:fold(
        fun(K, V2, Acc) ->
            case maps:find(K, Acc) of
                {ok, V1} when is_map(V1), is_map(V2) ->
                    Acc#{K := merge(V1, V2)};
                _ ->
                    Acc#{K => V2}
            end
        end,
        Map1,
        Map2
    ).

9.2 深度获取 #

erlang
-module(deep_get).
-export([get/2]).

get([], Map) -> Map;
get([Key | Rest], Map) when is_map(Map) ->
    case maps:find(Key, Map) of
        {ok, Value} -> get(Rest, Value);
        error -> undefined
    end;
get(_, _) -> undefined.

9.3 键转换 #

erlang
-module(key_transform).
-export([keys_to_atoms/1, keys_to_strings/1]).

keys_to_atoms(Map) ->
    maps:from_list([{to_atom(K), V} || {K, V} <- maps:to_list(Map)]).

keys_to_strings(Map) ->
    maps:from_list([{to_string(K), V} || {K, V} <- maps:to_list(Map)]).

to_atom(K) when is_atom(K) -> K;
to_atom(K) when is_binary(K) -> binary_to_atom(K, utf8);
to_atom(K) when is_list(K) -> list_to_atom(K).

to_string(K) when is_binary(K) -> K;
to_string(K) when is_atom(K) -> atom_to_binary(K, utf8);
to_string(K) when is_list(K) -> list_to_binary(K).

十、总结 #

本章学习了:

  • 映射的定义和特点
  • 映射操作函数
  • maps模块函数
  • 映射模式匹配
  • 实际应用案例
  • 映射与记录比较
  • 性能考虑
  • 实用函数

准备好学习记录类型了吗?让我们进入下一章。

最后更新:2026-03-27