@@ -227,6 +227,53 @@ func (m *Map[K, V]) GetOrSet(key K, value V) (actual V, loaded bool) {
227
227
return
228
228
}
229
229
230
+ // GetOrCompute is similar to GetOrSet but the value to be set is obtained from a constructor
231
+ // the value constructor is called only once
232
+ func (m * Map [K , V ]) GetOrCompute (key K , valueFn func () V ) (actual V , loaded bool ) {
233
+ var (
234
+ h = m .hasher (key )
235
+ data = m .metadata .Load ()
236
+ existing = data .indexElement (h )
237
+ )
238
+ // try to get the element if present
239
+ for elem := existing ; elem != nil && elem .keyHash <= h ; elem = elem .nextPtr .Load () {
240
+ if elem .key == key && ! elem .isDeleted () {
241
+ actual , loaded = * elem .value .Load (), true
242
+ return
243
+ }
244
+ }
245
+ // Get() failed because element is absent
246
+ // compute the value from the constructor and store it
247
+ value := valueFn ()
248
+ actual , loaded = value , false
249
+
250
+ var (
251
+ alloc * element [K , V ]
252
+ created = false
253
+ valPtr = & value
254
+ )
255
+ if existing == nil || existing .keyHash > h {
256
+ existing = m .listHead
257
+ }
258
+ if alloc , created = existing .inject (h , key , valPtr ); alloc != nil {
259
+ if created {
260
+ m .numItems .Add (1 )
261
+ }
262
+ } else {
263
+ for existing = m .listHead ; alloc == nil ; alloc , created = existing .inject (h , key , valPtr ) {
264
+ }
265
+ if created {
266
+ m .numItems .Add (1 )
267
+ }
268
+ }
269
+
270
+ count := data .addItemToIndex (alloc )
271
+ if resizeNeeded (uintptr (len (data .index )), count ) && m .resizing .CompareAndSwap (notResizing , resizingInProgress ) {
272
+ m .grow (0 ) // double in size
273
+ }
274
+ return
275
+ }
276
+
230
277
// CompareAndSwap atomically updates a map entry given its key by comparing current value to `oldValue`
231
278
// and setting it to `newValue` if the above comparison is successful
232
279
// It returns a boolean indicating whether the CompareAndSwap was successful or not
0 commit comments