Skip to content

Commit

Permalink
Use Set(T) instead of Hash(T, Bool) (#13611)
Browse files Browse the repository at this point in the history
  • Loading branch information
HertzDevil authored Jul 21, 2023
1 parent 84f389a commit d7a7df3
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 48 deletions.
53 changes: 17 additions & 36 deletions src/array.cr
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ class Array(T)
return Array(T).new if self.empty? || other.empty?

# Heuristic: for small arrays we do a linear scan, which is usually
# faster than creating an intermediate Hash.
# faster than creating an intermediate Set.
if self.size + other.size <= SMALL_ARRAY_SIZE * 2
ary = Array(T).new
each do |elem|
Expand All @@ -265,20 +265,13 @@ class Array(T)
return ary
end

hash = other.to_lookup_hash
hash_size = hash.size
set = other.to_set
Array(T).build(Math.min(size, other.size)) do |buffer|
i = 0
appender = buffer.appender
each do |obj|
hash.delete(obj)
new_hash_size = hash.size
if hash_size != new_hash_size
hash_size = new_hash_size
buffer[i] = obj
i += 1
end
appender << obj if set.delete(obj)
end
i
appender.size.to_i
end
end

Expand All @@ -292,7 +285,7 @@ class Array(T)
# See also: `#uniq`.
def |(other : Array(U)) : Array(T | U) forall U
# Heuristic: if the combined size is small we just do a linear scan
# instead of using a Hash for lookup.
# instead of using a Set for lookup.
if size + other.size <= SMALL_ARRAY_SIZE
ary = Array(T | U).new
each do |elem|
Expand All @@ -305,23 +298,15 @@ class Array(T)
end

Array(T | U).build(size + other.size) do |buffer|
hash = Hash(T, Bool).new
i = 0
set = Set(T).new
appender = buffer.appender
each do |obj|
unless hash.has_key?(obj)
buffer[i] = obj
hash[obj] = true
i += 1
end
appender << obj if set.add?(obj)
end
other.each do |obj|
unless hash.has_key?(obj)
buffer[i] = obj
hash[obj] = true
i += 1
end
appender << obj if set.add?(obj)
end
i
appender.size.to_i
end
end

Expand Down Expand Up @@ -356,7 +341,7 @@ class Array(T)
# ```
def -(other : Array(U)) : Array(T) forall U
# Heuristic: if any of the arrays is small we just do a linear scan
# instead of using a Hash for lookup.
# instead of using a Set for lookup.
if size <= SMALL_ARRAY_SIZE || other.size <= SMALL_ARRAY_SIZE
ary = Array(T).new
each do |elem|
Expand All @@ -366,9 +351,9 @@ class Array(T)
end

ary = Array(T).new(Math.max(size - other.size, 0))
hash = other.to_lookup_hash
set = other.to_set
each do |obj|
ary << obj unless hash.has_key?(obj)
ary << obj unless set.includes?(obj)
end
ary
end
Expand Down Expand Up @@ -1858,7 +1843,7 @@ class Array(T)
end

# Heuristic: for a small array it's faster to do a linear scan
# than creating a Hash to find out duplicates.
# than creating a Set to find out duplicates.
if size <= SMALL_ARRAY_SIZE
ary = Array(T).new
each do |elem|
Expand All @@ -1867,8 +1852,8 @@ class Array(T)
return ary
end

# Convert the Array into a Hash and then ask for its values
to_lookup_hash.values
# Convert the Array into a Set and then ask for its values
to_set.to_a
end

# Returns a new `Array` by removing duplicate values in `self`, using the block's
Expand Down Expand Up @@ -2176,10 +2161,6 @@ class Array(T)
Slice.new(@buffer + start, count)
end

protected def to_lookup_hash
to_lookup_hash { |elem| elem }
end

protected def to_lookup_hash(& : T -> U) forall U
each_with_object(Hash(U, T).new) do |o, h|
key = yield o
Expand Down
8 changes: 2 additions & 6 deletions src/iterator.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1283,18 +1283,14 @@ module Iterator(T)
include IteratorWrapper

def initialize(@iterator : I, @func : T -> U)
@hash = {} of U => Bool
@set = Set(U).new
end

def next
while true
value = wrapped_next
transformed = @func.call value

unless @hash[transformed]?
@hash[transformed] = true
return value
end
return value if @set.add?(transformed)
end
end
end
Expand Down
11 changes: 5 additions & 6 deletions src/reference.cr
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ class Reference

# :nodoc:
module ExecRecursive
alias Registry = Hash({UInt64, Symbol}, Bool)
# NOTE: can't use `Set` here because of prelude require order
alias Registry = Hash({UInt64, Symbol}, Nil)

{% if flag?(:preview_mt) %}
@@exec_recursive = Crystal::ThreadLocalValue(Registry).new
Expand All @@ -159,14 +160,12 @@ class Reference
private def exec_recursive(method, &)
hash = ExecRecursive.hash
key = {object_id, method}
if hash[key]?
false
else
hash[key] = true
hash.put(key, nil) do
yield
hash.delete(key)
true
return true
end
false
end

# :nodoc:
Expand Down

0 comments on commit d7a7df3

Please sign in to comment.