erlang的dict源码解析(2)

dict:filter()可以通过传入一个断言作为参数,来对目标dict达到筛选的目的。

filter(F, D) -> filter_dict(F, D).

filter_dict(F, #dict{size=0} = Dict) when is_function(F, 2) ->
    Dict;
filter_dict(F, D) ->
    Segs0 = tuple_to_list(D#dict.segs),
    {Segs1,Fc} = filter_seg_list(F, Segs0, [], 0),
    maybe_contract(D#dict{segs=list_to_tuple(Segs1)}, Fc).

filter_seg_list(F, [Seg|Segs], Fss, Fc0) ->
    Bkts0 = tuple_to_list(Seg),
    {Bkts1,Fc1} = filter_bkt_list(F, Bkts0, [], Fc0),
    filter_seg_list(F, Segs, [list_to_tuple(Bkts1)|Fss], Fc1);
filter_seg_list(F, [], Fss, Fc) when is_function(F, 2) ->
    {lists:reverse(Fss, []),Fc}.

filter_bkt_list(F, [Bkt0|Bkts], Fbs, Fc0) ->
    {Bkt1,Fc1} = filter_bucket(F, Bkt0, [], Fc0),
    filter_bkt_list(F, Bkts, [Bkt1|Fbs], Fc1);
filter_bkt_list(F, [], Fbs, Fc) when is_function(F, 2) ->
    {lists:reverse(Fbs),Fc}.

filter_bucket(F, [?kv(Key,Val)=E|Bkt], Fb, Fc) ->
    case F(Key, Val) of
 true -> filter_bucket(F, Bkt, [E|Fb], Fc);
 false -> filter_bucket(F, Bkt, Fb, Fc+1)
    end;
filter_bucket(F, [], Fb, Fc) when is_function(F, 2) ->
    {lists:reverse(Fb),Fc}.

首先将dict的segs转化为列表,依次从最前面的seg开始过滤。

在filter_seg_list()函数中,依次将seg化为列表,从列表的最开始依次在filter_bkt_list()中选择slot,将slot数据依次在filter_bucket()中根据断言的true or false放入结果列表的最前端,或者给表示未命中数的Fc加一。

在完成了一个slot的过滤后,翻转列表以便正序。而后选在单个seg列表的下一个slot进行过滤,同样,一个seg被过滤完后,同样也会将结果进行逆序,确保结果的正序。

完成seg的过滤后选择下一个seg,过程与前者类似,但是最后结果再返回前,将结果单次seg的过滤结果转化为元组。

在完成所有结果后,将结果转化为元组作为原来的dict的segs。

但是由于过滤后dict数据的减少,需要将返回的dict进行缩容。通过maybe_contract()进行,同时传入的还有代表刚才进行过滤而减少的元素个数Fc。

maybe_contract(T, Dc) when T#dict.size - Dc < T#dict.con_size,
      T#dict.n > ?seg_size ->
    N = T#dict.n,
    Slot1 = N - T#dict.bso,
    Segs0 = T#dict.segs,
    B1 = get_bucket_s(Segs0, Slot1),
    Slot2 = N,
    B2 = get_bucket_s(Segs0, Slot2),
    Segs1 = put_bucket_s(Segs0, Slot1, B1 ++ B2),
    Segs2 = put_bucket_s(Segs1, Slot2, []),  %Clear the upper bucket
    N1 = N - 1,
    maybe_contract_segs(T#dict{size=T#dict.size - Dc,
          n=N1,
          exp_size=N1 * ?expand_load,
          con_size=N1 * ?contract_load,
          segs=Segs2});
maybe_contract(T, Dc) -> T#dict{size=T#dict.size - Dc}.

maybe_contract_segs(T) when T#dict.n =:= T#dict.bso ->
    T#dict{maxn=T#dict.maxn div 2,
    bso=T#dict.bso div 2,
    segs=contract_segs(T#dict.segs)};
maybe_contract_segs(T) -> T.

如果原来dict的元素个数减去被过滤掉的个数仍旧大于需要缩容的大小con_size,那么直接减去并返回,否则准备开始扩容。

缩容的逻辑与扩容类似,只是代表当前活跃个数的N来减去偏移量bso代表Slot1,以便将最末尾的slot的数据放到Slot1所定位到的slot上,并将原本最末尾的slot的数据清空,N-1表示活跃的slot减少一个。

之后通过maybe_contract_segs()判断是否要将当前的dict的segs容量减半。

 

在完成缩容后,返回的dict就是过滤后的dict。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值