# Class: Concurrent::Map

Inherits:
  Collection::MapImplementation
  - Object
  - Concurrent::Map
  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::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.

## Instance Method Summary collapse

- #[](key) ⇒ Object (also: #get)

  Get a value with key.

- #[]=(key, value) ⇒ Object (also: #put)

  Set a value with key.

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

  Compute and store new value for key.

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

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

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

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

- #delete(key) ⇒ Object, nil

  Delete key and its value.

- #delete_pair(key, value) ⇒ true, false

  Delete pair and its value if current value equals the provided value.

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

  Iterates over each key.

- #each_pair {|key, value| ... } ⇒ self (also: #each)

  Iterates over each key value pair.

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

  Iterates over each value.

- #empty? ⇒ true, false

  Is map empty?.

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

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

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

- #get_and_set(key, value) ⇒ Object, nil

  Get the current value under key and set new value.

- #initialize(options = nil, &block) ⇒ Map constructor

  A new instance of Map.

- #key(value) ⇒ Object, nil (also: #index)

  Find key of a value.

- #keys ⇒ ::Array<Object>

  All keys.

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

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

- #put_if_absent(key, value) ⇒ Object, nil

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

- #replace_if_exists(key, new_value) ⇒ Object, nil

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

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

- #size ⇒ Integer

  The size of map.

- #value?(value) ⇒ true, false

  Is the value stored in the map.

- #values ⇒ ::Array<Object>

  All values.

## Constructor Details

### #initialize(options = nil, &block) ⇒ `Map`

Returns a new instance of Map.

    121
    122
    123
    124
    125
    126
    127
    128
    129
    130

    # 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

## Instance Method Details

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

Get a value with key

Parameters:

- key (`Object`)

Returns:

- (`Object`) —

  the value

    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147

    # 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

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

Set a value with key

Parameters:

- key (`Object`)
- value (`Object`)

Returns:

- (`Object`) —

  the new value

    153
    154
    155

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

    def []=(key, value)
      super
    end

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

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

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

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

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

### #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`)

    251
    252
    253

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

    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`)

    270
    271
    272
    273

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

    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`)

    260
    261
    262

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

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

### #empty? ⇒ `true`, `false`

Is map empty?

Returns:

- (`true`, `false`)

    288
    289
    290
    291

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

    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

    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188

    # 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| ... } ⇒ `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.

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. This method is atomic.

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

    201
    202
    203
    204
    205

    # 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_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 100

### #key(value) ⇒ `Object`, `nil` Also known as: index

Find key of a value.

Parameters:

- value (`Object`)

Returns:

- (`Object`, `nil`) —

  key or nil when not found

    280
    281
    282
    283

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

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

### #keys ⇒ `::Array<Object>`

All keys

Returns:

- (`::Array<Object>`) —

  keys

    232
    233
    234
    235
    236

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

    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 73

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

    211
    212
    213
    214
    215
    216
    217
    218

    # 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

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

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

### #size ⇒ `Integer`

The size of map.

Returns:

- (`Integer`) —

  size

    295
    296
    297
    298
    299

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

    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`)

    223
    224
    225
    226
    227
    228

    # 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 ⇒ `::Array<Object>`

All values

Returns:

- (`::Array<Object>`) —

  values

    240
    241
    242
    243
    244

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

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