Skip to content

Commit d311e10

Browse files
author
José Valim
committed
Change HashDict implementation to use tries with compound nodes
The new implementation is basically a tree of tuples (tries) but they allow compond nodes (a node with a key-value pair and a sub-tree). This removes the need for node-expansion and also simplifies the design structure to not require multiple stages nor buckets. The end result is a faster HashDict implementation (about 10% faster on access, 30% on updates), which is still small for small dicts but about 30% bigger for large dicts (in terms of memory) and with a much simpler implementation.
1 parent adf7834 commit d311e10

File tree

6 files changed

+175
-590
lines changed

6 files changed

+175
-590
lines changed

lib/elixir/lib/dict.ex

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,30 +42,30 @@ defmodule Dict do
4242

4343
@type key :: any
4444
@type value :: any
45-
@type keys :: [ key ]
4645
@type t :: tuple | list
4746

4847
defcallback new :: t
4948
defcallback new(Keyword.t) :: t
49+
defcallback new(Keyword.t, (any -> { key, value })) :: t
5050
defcallback delete(t, key) :: t
51-
defcallback drop(t, keys) :: t
51+
defcallback drop(t, [key]) :: t
5252
defcallback empty(t) :: t
5353
defcallback equal?(t, t) :: boolean
5454
defcallback get(t, key) :: value
5555
defcallback get(t, key, value) :: value
5656
defcallback fetch(t, key) :: { :ok, value } | :error
5757
defcallback fetch!(t, key) :: value | no_return
5858
defcallback has_key?(t, key) :: boolean
59-
defcallback keys(t) :: list(key)
59+
defcallback keys(t) :: [key]
6060
defcallback merge(t, t) :: t
6161
defcallback merge(t, t, (key, value, value -> value)) :: t
6262
defcallback pop(t, key) :: {value, t}
6363
defcallback pop(t, key, value) :: {value, t}
6464
defcallback put(t, key, value) :: t
6565
defcallback put_new(t, key, value) :: t
6666
defcallback size(t) :: non_neg_integer()
67-
defcallback split(t, keys) :: {t, t}
68-
defcallback take(t, keys) :: t
67+
defcallback split(t, [key]) :: {t, t}
68+
defcallback take(t, [key]) :: t
6969
defcallback to_list(t) :: list()
7070
defcallback update(t, key, value, (value -> value)) :: t
7171
defcallback update!(t, key, (value -> value)) :: t | no_return
@@ -381,7 +381,7 @@ defmodule Dict do
381381
382382
iex> d = dict_impl.new([a: 1, b: 2, c: 3, d: 4])
383383
...> { d1, d2 } = Dict.split(d, [:a, :c, :e])
384-
...> { Dict.to_list(d1), Dict.to_list(d2) }
384+
...> { Dict.to_list(d1) |> Enum.sort, Dict.to_list(d2) |> Enum.sort }
385385
{ [a: 1, c: 3], [b: 2, d: 4] }
386386
387387
iex> d = dict_impl.new([])
@@ -391,11 +391,11 @@ defmodule Dict do
391391
392392
iex> d = dict_impl.new([a: 1, b: 2])
393393
...> { d1, d2 } = Dict.split(d, [:a, :b, :c])
394-
...> { Dict.to_list(d1), Dict.to_list(d2) }
394+
...> { Dict.to_list(d1) |> Enum.sort, Dict.to_list(d2) }
395395
{ [a: 1, b: 2], [] }
396396
397397
"""
398-
@spec split(t, keys) :: {t, t}
398+
@spec split(t, [key]) :: {t, t}
399399
def split(dict, keys) do
400400
target(dict).split(dict, keys)
401401
end
@@ -413,11 +413,11 @@ defmodule Dict do
413413
414414
iex> d = dict_impl.new([a: 1, b: 2])
415415
...> d = Dict.drop(d, [:c, :d])
416-
...> Dict.to_list(d)
416+
...> Dict.to_list(d) |> Enum.sort
417417
[a: 1, b: 2]
418418
419419
"""
420-
@spec drop(t, keys) :: t
420+
@spec drop(t, [key]) :: t
421421
def drop(dict, keys) do
422422
target(dict).drop(dict, keys)
423423
end
@@ -440,7 +440,7 @@ defmodule Dict do
440440
[]
441441
442442
"""
443-
@spec take(t, keys) :: t
443+
@spec take(t, [key]) :: t
444444
def take(dict, keys) do
445445
target(dict).take(dict, keys)
446446
end

lib/elixir/lib/enum.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ defmodule Enum do
145145
enumeration. For dicts, the argument is always a `{ key, value }` tuple:
146146
147147
iex> dict = HashDict.new [a: 1, b: 2]
148-
iex> Enum.map(dict, fn { k, v } -> { k, v * 2 } end)
148+
iex> Enum.map(dict, fn { k, v } -> { k, v * 2 } end) |> Enum.sort
149149
[a: 2, b: 4]
150150
151151
Note that the functions in the `Enum` module are eager: they always start

0 commit comments

Comments
 (0)