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。