123456789_123456789_123456789_123456789_123456789_

Class: Concurrent::Map

Relationships & Source Files
Inherits: Concurrent::Collection::MapImplementation
Defined in: lib/concurrent-ruby/concurrent/map.rb

Overview

Map is a hash-like object and should have much better performance characteristics, especially under high concurrency, than 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 Map instead of Hash for your concurrency-safe hash needs.

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(options = nil, &default_proc) ⇒ Map

Parameters:

  • options (Hash, nil) (defaults to: nil)

    options to set the :initial_capacity or :load_factor. Ignored on some Rubies.

  • default_proc (Proc)

    Optional block to compute the default value if the key is not set, like Hash#default_proc

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/map.rb', line 133

def initialize(options = nil, &default_proc)
  if options.kind_of?(::Hash)
    validate_options_hash!(options)
  else
    options = nil
  end

  super(options)
  @default_proc = default_proc
end

Instance Attribute Details

#empty?true, false (readonly)

Is map empty?

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/map.rb', line 291

def empty?
  each_pair { |k, v| return false }
  true
end

Instance Method Details

#[](key) ⇒ Object Also known as: #get

Get a value with key

Parameters:

Returns:

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/map.rb', line 147

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

#[]=(key, value) ⇒ Object Also known as: #put

Set a value with key

Parameters:

Returns:

[ GitHub ]

#compute(key) {|old_value| ... } ⇒ Object?

Note:

Atomic methods taking a block do not allow the self instance to be used within the block. Doing so will cause a deadlock.

Compute and store new value for key. This method is atomic.

Parameters:

Yields:

  • compute new value from old one

Yield Parameters:

  • old_value (Object, nil)

    old_value, or nil when key is absent

Yield Returns:

  • (Object, nil)

    new value, when nil the key is removed

Returns:

  • (Object, nil)

    new value or nil

[ GitHub ]

#compute_if_absent(key) { ... } ⇒ Object

Note:

Atomic methods taking a block do not allow the self instance to be used within the block. Doing so will cause a deadlock.

Compute and store new value for key if the key is absent. This method is atomic.

Parameters:

Yields:

  • new value

Yield Returns:

Returns:

  • (Object)

    new value or current value

[ GitHub ]

#compute_if_present(key) {|old_value| ... } ⇒ Object?

Note:

Atomic methods taking a block do not allow the self instance to be used within the block. Doing so will cause a deadlock.

Compute and store new value for key if the key is present. This method is atomic.

Parameters:

Yields:

  • new value

Yield Parameters:

Yield Returns:

  • (Object, nil)

    new value, when nil the key is removed

Returns:

  • (Object, nil)

    new value or nil

[ GitHub ]

#delete(key) ⇒ Object?

Delete key and its value. This method is atomic.

Parameters:

Returns:

  • (Object, nil)

    old value or nil when the key was absent

[ GitHub ]

#delete_pair(key, value) ⇒ true, false

Delete pair and its value if current value equals the provided value. This method is atomic.

Parameters:

Returns:

  • (true, false)

    true if deleted

[ GitHub ]

#each

Alias for #each_pair.

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/map.rb', line 279

alias_method :each, :each_pair unless method_defined?(:each)

#each_key {|key| ... } ⇒ self

Note:

Atomic methods taking a block do not allow the self instance to be used within the block. Doing so will cause a deadlock.

Iterates over each key. This method is atomic.

Yields:

  • for each key in the map

Yield Parameters:

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/map.rb', line 255

def each_key
  each_pair { |k, v| yield k }
end

#each_pair {|key, value| ... } ⇒ self Also known as: #each, #each

Note:

Atomic methods taking a block do not allow the self instance to be used within the block. Doing so will cause a deadlock.

Iterates over each key value pair. This method is atomic.

Yields:

  • for each key value pair in the map

Yield Parameters:

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/map.rb', line 274

def each_pair
  return enum_for :each_pair unless block_given?
  super
end

#each_value {|value| ... } ⇒ self

Note:

Atomic methods taking a block do not allow the self instance to be used within the block. Doing so will cause a deadlock.

Iterates over each value. This method is atomic.

Yields:

  • for each value in the map

Yield Parameters:

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/map.rb', line 264

def each_value
  each_pair { |k, v| yield v }
end

#fetch(key, default_value = NULL) {|key| ... } ⇒ Object

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.

Get a value with key, or default_value when key is absent, or fail when no default_value is given.

Parameters:

Yields:

  • default value for a key

Yield Parameters:

Yield Returns:

Returns:

  • (Object)

    the value or default value

Raises:

  • (KeyError)

    when key is missing and no default_value is provided

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/map.rb', line 183

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| ... } ⇒ Object

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.

Parameters:

Yields:

  • default value for a key

Yield Parameters:

Yield Returns:

Returns:

  • (Object)

    the value or default value

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/map.rb', line 205

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 #[].

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/map.rb', line 162

alias_method :get, :[]

#get_and_set(key, value) ⇒ Object?

Get the current value under key and set new value. This method is atomic.

Parameters:

Returns:

  • (Object, nil)

    old value or nil when the key was absent

[ GitHub ]

#initialize_copy(other) (private)

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/map.rb', line 331

def initialize_copy(other)
  super
  populate_from(other)
end

#inspect (private)

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/map.rb', line 321

def inspect
  format '%s entries=%d default_proc=%s>', to_s[0..-2], size.to_s, @default_proc.inspect
end

#key(value) ⇒ Object?

Find key of a value.

Parameters:

Returns:

  • (Object, nil)

    key or nil when not found

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/map.rb', line 284

def key(value)
  each_pair { |k, v| return k if v == value }
  nil
end

#keys::Array<Object>

All keys

Returns:

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/map.rb', line 236

def keys
  arr = []
  each_pair { |k, v| arr << k }
  arr
end

#marshal_dump (private)

Raises:

  • (TypeError)
[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/map.rb', line 305

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) (private)

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/map.rb', line 313

def marshal_load(hash)
  initialize
  populate_from(hash)
end

#merge_pair(key, value) {|old_value| ... } ⇒ Object?

Note:

Atomic methods taking a block do not allow the self instance to be used within the block. Doing so will cause a deadlock.

If the key is absent, the value is stored, otherwise new value is computed with a block. This method is atomic.

Parameters:

Yields:

  • compute new value from old one

Yield Parameters:

  • old_value (Object)

    old value

Yield Returns:

  • (Object, nil)

    new value, when nil the key is removed

Returns:

  • (Object, nil)

    new value or nil

[ GitHub ]

#populate_from(hash) (private)

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/map.rb', line 336

def populate_from(hash)
  hash.each_pair { |k, v| self[k] = v }
  self
end

#put(key, value)

Alias for #[]=.

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/map.rb', line 163

alias_method :put, :[]=

#put_if_absent(key, value) ⇒ Object?

Insert value into map with key if key is absent in one atomic step.

Parameters:

Returns:

  • (Object, nil)

    the previous value when key was present or nil when there was no key

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/map.rb', line 215

def put_if_absent(key, value)
  computed = false
  result   = compute_if_absent(key) do
    computed = true
    value
  end
  computed ? nil : result
end

#raise_fetch_no_key (private)

Raises:

  • (KeyError)
[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/map.rb', line 327

def raise_fetch_no_key
  raise KeyError, 'key not found'
end

#replace_if_exists(key, new_value) ⇒ Object?

Replaces current value with new_value if key exists This method is atomic.

Parameters:

Returns:

  • (Object, nil)

    old value or nil

[ GitHub ]

#replace_pair(key, old_value, new_value) ⇒ true, false

Replaces old_value with new_value if key exists and current value matches old_value This method is atomic.

Parameters:

Returns:

  • (true, false)

    true if replaced

[ GitHub ]

#sizeInteger

The size of map.

Returns:

  • (Integer)

    size

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/map.rb', line 298

def size
  count = 0
  each_pair { |k, v| count += 1 }
  count
end

#validate_options_hash!(options) (private)

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/map.rb', line 341

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

#value?(value) ⇒ true, false

Is the value stored in the map. Iterates over all values.

Parameters:

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/map.rb', line 227

def value?(value)
  each_value do |v|
    return true if value.equal?(v)
  end
  false
end

#values::Array<Object>

All values

Returns:

  • (::Array<Object>)

    values

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/map.rb', line 244

def values
  arr = []
  each_pair { |k, v| arr << v }
  arr
end