class Concurrent::Map
`Concurrent::Map` is a hash-like object and should have much better performance characteristics, especially under high concurrency, than `Concurrent::Hash`. However, `Concurrent::Map `is not strictly semantically equivalent to a ruby `Hash` – for instance, it does not necessarily retain ordering by insertion time as `Hash` does. For most uses it should do fine though, and we recommend you consider `Concurrent::Map` instead of `Concurrent::Hash` for your concurrency-safe hash needs.
Public Class Methods
# File lib/concurrent-ruby/concurrent/map.rb, line 121 def initialize(options = nil, &block) if options.kind_of?(::Hash) validate_options_hash!(options) else options = nil end super(options) @default_proc = block end
Public Instance Methods
Get a value with key @param [Object] key @return [Object] the value
# File lib/concurrent-ruby/concurrent/map.rb, line 135 def [](key) if value = super # non-falsy value is an existing mapping, return it right away value # re-check is done with get_or_default(key, NULL) instead of a simple !key?(key) in order to avoid a race condition, whereby by the time the current thread gets to the key?(key) call # a key => value mapping might have already been created by a different thread (key?(key) would then return true, this elsif branch wouldn't be taken and an incorrent +nil+ value # would be returned) # note: nil == value check is not technically necessary elsif @default_proc && nil == value && NULL == (value = get_or_default(key, NULL)) @default_proc.call(self, key) else value end end
Set
a value with key @param [Object] key @param [Object] value @return [Object] the new value
# File lib/concurrent-ruby/concurrent/map.rb, line 153 def []=(key, value) super end
Iterates over each key. @yield for each key in the map @yieldparam key [Object] @return [self] @!macro map.atomic_method_with_block
# File lib/concurrent-ruby/concurrent/map.rb, line 251 def each_key each_pair { |k, v| yield k } end
Iterates over each key value pair. @yield for each key value pair in the map @yieldparam key [Object] @yieldparam value [Object] @return [self] @!macro map.atomic_method_with_block
# File lib/concurrent-ruby/concurrent/map.rb, line 270 def each_pair return enum_for :each_pair unless block_given? super end
Iterates over each value. @yield for each value in the map @yieldparam value [Object] @return [self] @!macro map.atomic_method_with_block
# File lib/concurrent-ruby/concurrent/map.rb, line 260 def each_value each_pair { |k, v| yield v } end
Is map empty? @return [true, false]
# File lib/concurrent-ruby/concurrent/map.rb, line 287 def empty? each_pair { |k, v| return false } true end
Get a value with key, or default_value when key is absent, or fail when no default_value is given. @param [Object] key @param [Object] default_value @yield default value for a key @yieldparam key [Object] @yieldreturn [Object] default value @return [Object] the value or default value @raise [KeyError] when key is missing and no default_value is provided @!macro map_method_not_atomic
@note The "fetch-then-act" methods of `Map` are not atomic. `Map` is intended to be use as a concurrency primitive with strong happens-before guarantees. It is not intended to be used as a high-level abstraction supporting complex operations. All read and write operations are thread safe, but no guarantees are made regarding race conditions between the fetch operation and yielding to the block. Additionally, this method does not support recursion. This is due to internal constraints that are very unlikely to change in the near future.
# File lib/concurrent-ruby/concurrent/map.rb, line 178 def fetch(key, default_value = NULL) if NULL != (value = get_or_default(key, NULL)) value elsif block_given? yield key elsif NULL != default_value default_value else raise_fetch_no_key end end
Fetch value with key, or store default value when key is absent, or fail when no default_value is given. This is a two step operation, therefore not atomic. The store can overwrite other concurrently stored value. @param [Object] key @param [Object] default_value @yield default value for a key @yieldparam key [Object] @yieldreturn [Object] default value @return [Object] the value or default value @!macro map.atomic_method_with_block
# File lib/concurrent-ruby/concurrent/map.rb, line 201 def fetch_or_store(key, default_value = NULL) fetch(key) do put(key, block_given? ? yield(key) : (NULL == default_value ? raise_fetch_no_key : default_value)) end end
@!visibility private
# File lib/concurrent-ruby/concurrent/map.rb, line 317 def inspect format '%s entries=%d default_proc=%s>', to_s[0..-2], size.to_s, @default_proc.inspect end
Find key of a value. @param [Object] value @return [Object, nil] key or nil when not found
# File lib/concurrent-ruby/concurrent/map.rb, line 280 def key(value) each_pair { |k, v| return k if v == value } nil end
All keys @return [::Array<Object>] keys
# File lib/concurrent-ruby/concurrent/map.rb, line 232 def keys arr = [] each_pair { |k, v| arr << k } arr end
@!visibility private
# File lib/concurrent-ruby/concurrent/map.rb, line 301 def marshal_dump raise TypeError, "can't dump hash with default proc" if @default_proc h = {} each_pair { |k, v| h[k] = v } h end
@!visibility private
# File lib/concurrent-ruby/concurrent/map.rb, line 309 def marshal_load(hash) initialize populate_from(hash) end
Insert value into map with key if key is absent in one atomic step. @param [Object] key @param [Object] value @return [Object, nil] the previous value when key was present or nil when there was no key
# File lib/concurrent-ruby/concurrent/map.rb, line 211 def put_if_absent(key, value) computed = false result = compute_if_absent(key) do computed = true value end computed ? nil : result end
The size of map. @return [Integer] size
# File lib/concurrent-ruby/concurrent/map.rb, line 294 def size count = 0 each_pair { |k, v| count += 1 } count end
Is the value stored in the map. Iterates over all values. @param [Object] value @return [true, false]
# File lib/concurrent-ruby/concurrent/map.rb, line 223 def value?(value) each_value do |v| return true if value.equal?(v) end false end
All values @return [::Array<Object>] values
# File lib/concurrent-ruby/concurrent/map.rb, line 240 def values arr = [] each_pair { |k, v| arr << v } arr end
Private Instance Methods
# File lib/concurrent-ruby/concurrent/map.rb, line 327 def initialize_copy(other) super populate_from(other) end
# File lib/concurrent-ruby/concurrent/map.rb, line 332 def populate_from(hash) hash.each_pair { |k, v| self[k] = v } self end
# File lib/concurrent-ruby/concurrent/map.rb, line 323 def raise_fetch_no_key raise KeyError, 'key not found' end
# File lib/concurrent-ruby/concurrent/map.rb, line 337 def validate_options_hash!(options) if (initial_capacity = options[:initial_capacity]) && (!initial_capacity.kind_of?(Integer) || initial_capacity < 0) raise ArgumentError, ":initial_capacity must be a positive Integer" end if (load_factor = options[:load_factor]) && (!load_factor.kind_of?(Numeric) || load_factor <= 0 || load_factor > 1) raise ArgumentError, ":load_factor must be a number between 0 and 1" end end