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.

> require 'concurrent' > > map = ::new

Constants

DEFAULT_OBJ_ID_STR_WIDTH

@!visibility private

Public Class Methods

new(options = nil, &block) click to toggle source

@!method delete_pair

@!macro map_method_is_atomic
Calls superclass method
# File lib/concurrent/map.rb, line 81
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
Calls superclass method
# File lib/concurrent/map.rb, line 92
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
each_key() { |k| ... } click to toggle source
# File lib/concurrent/map.rb, line 166
def each_key
  each_pair {|k, v| yield k}
end
each_value() { |v| ... } click to toggle source
# File lib/concurrent/map.rb, line 170
def each_value
  each_pair {|k, v| yield v}
end
empty?() click to toggle source
# File lib/concurrent/map.rb, line 182
def empty?
  each_pair {|k, v| return false}
  true
end
fetch(key, default_value = NULL) { |key| ... } click to toggle source

@!macro [attach] map_method_not_atomic

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/map.rb, line 118
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

@!macro map_method_not_atomic

# File lib/concurrent/map.rb, line 131
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: []
index(value)
Alias for: key
inspect() click to toggle source

override default inspect() method: firstly, we don't want to be spilling our guts (i-vars), secondly, MRI backend's inspect() call on its @backend i-var will bump @backend's iter level while possibly yielding GVL

# File lib/concurrent/map.rb, line 211
def inspect
  id_str = (object_id << 1).to_s(16).rjust(DEFAULT_OBJ_ID_STR_WIDTH, '0')
  "#<#{self.class.name}:0x#{id_str} entries=#{size} default_proc=#{@default_proc.inspect}>"
end
key(value) click to toggle source
# File lib/concurrent/map.rb, line 176
def key(value)
  each_pair {|k, v| return k if v == value}
  nil
end
Also aliased as: index
keys() click to toggle source
# File lib/concurrent/map.rb, line 154
def keys
  arr = []
  each_pair {|k, v| arr << k}
  arr
end
marshal_dump() click to toggle source
# File lib/concurrent/map.rb, line 193
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
# File lib/concurrent/map.rb, line 200
def marshal_load(hash)
  initialize
  populate_from(hash)
end
put_if_absent(key, value) click to toggle source

@!macro map_method_is_atomic

# File lib/concurrent/map.rb, line 138
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
# File lib/concurrent/map.rb, line 187
def size
  count = 0
  each_pair {|k, v| count += 1}
  count
end
value?(value) click to toggle source
# File lib/concurrent/map.rb, line 147
def value?(value)
  each_value do |v|
    return true if value.equal?(v)
  end
  false
end
values() click to toggle source
# File lib/concurrent/map.rb, line 160
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/map.rb, line 221
def initialize_copy(other)
  super
  populate_from(other)
end
populate_from(hash) click to toggle source
# File lib/concurrent/map.rb, line 226
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/map.rb, line 217
def raise_fetch_no_key
  raise KeyError, 'key not found'
end
validate_options_hash!(options) click to toggle source
# File lib/concurrent/map.rb, line 231
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