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

new(options = nil, &block) click to toggle source
Calls superclass method
# 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

[](key) click to toggle source

Get a value with key @param [Object] key @return [Object] the value

Calls superclass method
# 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
Also aliased as: get
[]=(key, value) click to toggle source

Set a value with key @param [Object] key @param [Object] value @return [Object] the new value

Calls superclass method
# File lib/concurrent-ruby/concurrent/map.rb, line 153
def []=(key, value)
  super
end
Also aliased as: put
each()
Alias for: each_pair
each_key() { |k| ... } click to toggle source

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
each_pair() click to toggle source

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

Calls superclass method
# File lib/concurrent-ruby/concurrent/map.rb, line 270
def each_pair
  return enum_for :each_pair unless block_given?
  super
end
Also aliased as: each
each_value() { |v| ... } click to toggle source

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
empty?() click to toggle source

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
fetch(key, default_value = NULL) { |key| ... } click to toggle source

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_or_store(key, default_value = NULL) { |key| ... } click to toggle source

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
get(key)
Alias for: []
inspect() click to toggle source

@!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
key(value) click to toggle source

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
keys() click to toggle source

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
marshal_dump() click to toggle source

@!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
marshal_load(hash) click to toggle source

@!visibility private

# File lib/concurrent-ruby/concurrent/map.rb, line 309
def marshal_load(hash)
  initialize
  populate_from(hash)
end
put(key, value)
Alias for: []=
put_if_absent(key, value) click to toggle source

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
size() click to toggle source

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
value?(value) click to toggle source

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
values() click to toggle source

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

initialize_copy(other) click to toggle source
Calls superclass method
# File lib/concurrent-ruby/concurrent/map.rb, line 327
def initialize_copy(other)
  super
  populate_from(other)
end
populate_from(hash) click to toggle source
# File lib/concurrent-ruby/concurrent/map.rb, line 332
def populate_from(hash)
  hash.each_pair { |k, v| self[k] = v }
  self
end
raise_fetch_no_key() click to toggle source
# File lib/concurrent-ruby/concurrent/map.rb, line 323
def raise_fetch_no_key
  raise KeyError, 'key not found'
end
validate_options_hash!(options) click to toggle source
# 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