Class: Concurrent::Map

Inherits:
Collection::MapImplementation
  • Object
show all
Defined in:
lib/concurrent-ruby/concurrent/map.rb

Overview

Concurrent::Map is a hash-like object and should have much better performance characteristics, especially under high concurrency, than Concurrent::Hash. However, Concurrent::Mapis 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.

Instance Method Summary collapse

Constructor Details

#initialize(options = nil, &default_proc) ⇒ Map

Returns a new instance of 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

 133 134 135 136 137 138 139 140 141 142
# 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 Method Details

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

Get a value with key

Parameters:

  • key (Object)

Returns:

  • (Object)

    the value

 147 148 149 150 151 152 153 154 155 156 157 158 159
# 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 incorrect +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:

  • key (Object)
  • value (Object)

Returns:

  • (Object)

    the new value

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

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

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:

  • key (Object)

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

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

#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:

  • key (Object)

Yields:

  • new value

Yield Returns:

  • (Object)

    new value

Returns:

  • (Object)

    new value or current value

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

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

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:

  • key (Object)

Yields:

  • new value

Yield Parameters:

  • old_value (Object)

Yield Returns:

  • (Object, nil)

    new value, when nil the key is removed

Returns:

  • (Object, nil)

    new value or nil

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

#delete(key) ⇒ Object, nil

Delete key and its value. This method is atomic.

Parameters:

  • key (Object)

Returns:

  • (Object, nil)

    old value or nil when the key was absent

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

#delete_pair(key, value) ⇒ true, false

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

Parameters:

  • key (Object)
  • value (Object)

Returns:

  • (true, false)

    true if deleted

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

#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:

  • key (Object)

Returns:

  • (self)
 255 256 257
# 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

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:

  • key (Object)
  • value (Object)

Returns:

  • (self)
 274 275 276 277
# 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:

  • value (Object)

Returns:

  • (self)
 264 265 266
# File 'lib/concurrent-ruby/concurrent/map.rb', line 264 def each_value each_pair { |k, v| yield v } end

#empty?true, false

Is map empty?

Returns:

  • (true, false)
 291 292 293 294
# File 'lib/concurrent-ruby/concurrent/map.rb', line 291 def empty? each_pair { |k, v| return false } true 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:

  • key (Object)
  • default_value (Object) (defaults to: NULL)

Yields:

  • default value for a key

Yield Parameters:

  • key (Object)

Yield Returns:

  • (Object)

    default value

Returns:

  • (Object)

    the value or default value

Raises:

  • (KeyError)

    when key is missing and no default_value is provided

 183 184 185 186 187 188 189 190 191 192 193
# 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:

  • key (Object)
  • default_value (Object) (defaults to: NULL)

Yields:

  • default value for a key

Yield Parameters:

  • key (Object)

Yield Returns:

  • (Object)

    default value

Returns:

  • (Object)

    the value or default value

 205 206 207 208 209
# 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_and_set(key, value) ⇒ Object, nil

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

Parameters:

  • key (Object)
  • value (Object)

Returns:

  • (Object, nil)

    old value or nil when the key was absent

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

#key(value) ⇒ Object, nil

Find key of a value.

Parameters:

  • value (Object)

Returns:

  • (Object, nil)

    key or nil when not found

 284 285 286 287
# 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:

  • (::Array<Object>)

    keys

 236 237 238 239 240
# File 'lib/concurrent-ruby/concurrent/map.rb', line 236 def keys arr = [] each_pair { |k, v| arr << k } arr end

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

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:

  • key (Object)
  • value (Object)

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

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

#put_if_absent(key, value) ⇒ Object, nil

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

Parameters:

  • key (Object)
  • value (Object)

Returns:

  • (Object, nil)

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

 215 216 217 218 219 220 221 222
# 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

#replace_if_exists(key, new_value) ⇒ Object, nil

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

Parameters:

  • key (Object)
  • new_value (Object)

Returns:

  • (Object, nil)

    old value or nil

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

#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:

  • key (Object)
  • old_value (Object)
  • new_value (Object)

Returns:

  • (true, false)

    true if replaced

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

#sizeInteger

The size of map.

Returns:

  • (Integer)

    size

 298 299 300 301 302
# File 'lib/concurrent-ruby/concurrent/map.rb', line 298 def size count = 0 each_pair { |k, v| count += 1 } count end

#value?(value) ⇒ true, false

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

Parameters:

  • value (Object)

Returns:

  • (true, false)
 227 228 229 230 231 232
# 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

 244 245 246 247 248
# File 'lib/concurrent-ruby/concurrent/map.rb', line 244 def values arr = [] each_pair { |k, v| arr << v } arr end