2 // ConcurrentSkipList.cs
4 // Copyright (c) 2009 Jérémie "Garuma" Laval
6 // Permission is hereby granted, free of charge, to any person obtaining a copy
7 // of this software and associated documentation files (the "Software"), to deal
8 // in the Software without restriction, including without limitation the rights
9 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 // copies of the Software, and to permit persons to whom the Software is
11 // furnished to do so, subject to the following conditions:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 using System.Threading;
28 using System.Collections;
29 using System.Collections.Generic;
30 using System.Runtime.Serialization;
32 namespace System.Collections.Concurrent
34 public class ConcurrentDictionary<TKey, TValue> : IDictionary<TKey, TValue>,
35 ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>,
36 IDictionary, ICollection, IEnumerable
40 public readonly TKey Key;
43 public Pair (TKey key, TValue value)
49 public override bool Equals (object obj)
51 Pair rhs = obj as Pair;
52 return rhs == null ? false : Key.Equals (rhs.Key) && Value.Equals (rhs.Value);
55 public override int GetHashCode ()
57 return Key.GetHashCode ();
61 class Basket: List<Pair>
65 // Assumption: a List<T> is never empty
66 ConcurrentSkipList<Basket> container
67 = new ConcurrentSkipList<Basket> ((value) => value[0].GetHashCode ());
69 IEqualityComparer<TKey> comparer;
71 public ConcurrentDictionary () : this (EqualityComparer<TKey>.Default)
75 public ConcurrentDictionary (IEnumerable<KeyValuePair<TKey, TValue>> values)
76 : this (values, EqualityComparer<TKey>.Default)
78 foreach (KeyValuePair<TKey, TValue> pair in values)
79 Add (pair.Key, pair.Value);
82 public ConcurrentDictionary (IEqualityComparer<TKey> comparer)
84 this.comparer = comparer;
87 public ConcurrentDictionary (IEnumerable<KeyValuePair<TKey, TValue>> values, IEqualityComparer<TKey> comparer)
90 foreach (KeyValuePair<TKey, TValue> pair in values)
91 Add (pair.Key, pair.Value);
95 public ConcurrentDictionary (int concurrencyLevel, int capacity)
96 : this (EqualityComparer<TKey>.Default)
101 public ConcurrentDictionary (int concurrencyLevel,
102 IEnumerable<KeyValuePair<TKey, TValue>> values,
103 IEqualityComparer<TKey> comparer)
104 : this (values, comparer)
110 public ConcurrentDictionary (int concurrencyLevel, int capacity, IEqualityComparer<TKey> comparer)
116 void Add (TKey key, TValue value)
118 while (!TryAdd (key, value));
121 void IDictionary<TKey, TValue>.Add (TKey key, TValue value)
126 public bool TryAdd (TKey key, TValue value)
129 // Add a value to an existing basket
130 if (TryGetBasket (key, out basket)) {
131 // Find a maybe more sexy locking scheme later
133 foreach (var p in basket) {
134 if (comparer.Equals (p.Key, key))
135 throw new ArgumentException ("An element with the same key already exists");
137 basket.Add (new Pair (key, value));
141 basket = new Basket ();
142 basket.Add (new Pair (key, value));
144 if (container.TryAdd (basket)) {
145 Interlocked.Increment (ref count);
152 Interlocked.Increment (ref count);
157 void ICollection<KeyValuePair<TKey,TValue>>.Add (KeyValuePair<TKey, TValue> pair)
159 Add (pair.Key, pair.Value);
162 public TValue AddOrUpdate (TKey key, Func<TKey, TValue> addValueFactory, Func<TKey, TValue, TValue> updateValueFactory)
167 if (!TryGetBasket (key, out basket)) {
168 Add (key, (temp = addValueFactory (key)));
171 Pair pair = basket.Find ((p) => comparer.Equals (p.Key, key));
173 throw new InvalidOperationException ("pair is null, shouldn't be");
174 pair.Value = (temp = updateValueFactory (key, pair.Value));
181 public TValue AddOrUpdate (TKey key, TValue addValue, Func<TKey, TValue, TValue> updateValueFactory)
183 return AddOrUpdate (key, (_) => addValue, updateValueFactory);
186 TValue GetValue (TKey key)
189 if (!TryGetValue (key, out temp))
190 // TODO: find a correct Exception
191 throw new ArgumentException ("Not a valid key for this dictionary", "key");
195 public bool TryGetValue (TKey key, out TValue value)
198 value = default (TValue);
200 if (!TryGetBasket (key, out basket))
204 Pair pair = basket.Find ((p) => comparer.Equals (p.Key, key));
213 public bool TryUpdate (TKey key, TValue newValue, TValue comparand)
216 if (!TryGetBasket (key, out basket))
220 Pair pair = basket.Find ((p) => comparer.Equals (p.Key, key));
221 if (pair.Value.Equals (comparand)) {
222 pair.Value = newValue;
231 public TValue this[TKey key] {
233 return GetValue (key);
237 if (!TryGetBasket (key, out basket)) {
242 Pair pair = basket.Find ((p) => comparer.Equals (p.Key, key));
244 throw new InvalidOperationException ("pair is null, shouldn't be");
250 public TValue GetOrAdd (TKey key, Func<TKey, TValue> valueFactory)
253 TValue temp = default (TValue);
255 if (TryGetBasket (key, out basket)) {
258 pair = basket.Find ((p) => comparer.Equals (p.Key, key));
264 Add (key, (temp = valueFactory (key)));
266 Add (key, (temp = valueFactory (key)));
272 public TValue GetOrAdd (TKey key, TValue value)
274 return GetOrAdd (key, (_) => value);
277 public bool TryRemove(TKey key, out TValue value)
279 value = default (TValue);
282 if (!TryGetBasket (key, out b))
286 TValue temp = default (TValue);
287 // Should always be == 1 but who know
288 bool result = b.RemoveAll ((p) => {
289 bool r = comparer.Equals (p.Key, key);
290 if (r) temp = p.Value;
296 Interlocked.Decrement (ref count);
302 bool Remove (TKey key)
306 return TryRemove (key, out dummy);
309 bool IDictionary<TKey, TValue>.Remove (TKey key)
314 bool ICollection<KeyValuePair<TKey,TValue>>.Remove (KeyValuePair<TKey,TValue> pair)
316 return Remove (pair.Key);
319 public bool ContainsKey (TKey key)
321 return container.ContainsFromHash (key.GetHashCode ());
324 bool IDictionary.Contains (object key)
329 return ContainsKey ((TKey)key);
332 void IDictionary.Remove (object key)
340 object IDictionary.this [object key]
344 throw new ArgumentException ("key isn't of correct type", "key");
346 return this[(TKey)key];
349 if (!(key is TKey) || !(value is TValue))
350 throw new ArgumentException ("key or value aren't of correct type");
352 this[(TKey)key] = (TValue)value;
356 void IDictionary.Add (object key, object value)
358 if (!(key is TKey) || !(value is TValue))
359 throw new ArgumentException ("key or value aren't of correct type");
361 Add ((TKey)key, (TValue)value);
364 bool ICollection<KeyValuePair<TKey,TValue>>.Contains (KeyValuePair<TKey, TValue> pair)
366 return ContainsKey (pair.Key);
369 public KeyValuePair<TKey,TValue>[] ToArray ()
371 // This is most certainly not optimum but there is
372 // not a lot of possibilities
374 return new List<KeyValuePair<TKey,TValue>> (this).ToArray ();
380 container = new ConcurrentSkipList<Basket> ((value) => value [0].GetHashCode ());
389 public bool IsEmpty {
395 bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly {
401 bool IDictionary.IsReadOnly {
407 public ICollection<TKey> Keys {
409 return GetPart<TKey> ((kvp) => kvp.Key);
413 public ICollection<TValue> Values {
415 return GetPart<TValue> ((kvp) => kvp.Value);
419 ICollection IDictionary.Keys {
421 return (ICollection)Keys;
425 ICollection IDictionary.Values {
427 return (ICollection)Values;
431 ICollection<T> GetPart<T> (Func<KeyValuePair<TKey, TValue>, T> extractor)
433 List<T> temp = new List<T> ();
435 foreach (KeyValuePair<TKey, TValue> kvp in this)
436 temp.Add (extractor (kvp));
438 return temp.AsReadOnly ();
441 void ICollection.CopyTo (Array array, int startIndex)
443 KeyValuePair<TKey, TValue>[] arr = array as KeyValuePair<TKey, TValue>[];
447 CopyTo (arr, startIndex, count);
450 void CopyTo (KeyValuePair<TKey, TValue>[] array, int startIndex)
452 CopyTo (array, startIndex, count);
455 void ICollection<KeyValuePair<TKey, TValue>>.CopyTo (KeyValuePair<TKey, TValue>[] array, int startIndex)
457 CopyTo (array, startIndex);
460 void CopyTo (KeyValuePair<TKey, TValue>[] array, int startIndex, int num)
462 // TODO: This is quite unsafe as the count value will likely change during
463 // the copying. Watchout for IndexOutOfRange thingies
464 if (array.Length <= count + startIndex)
465 throw new InvalidOperationException ("The array isn't big enough");
469 foreach (Basket b in container) {
471 foreach (Pair p in b) {
474 array[i++] = new KeyValuePair<TKey, TValue> (p.Key, p.Value);
480 public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator ()
482 return GetEnumeratorInternal ();
485 IEnumerator IEnumerable.GetEnumerator ()
487 return (IEnumerator)GetEnumeratorInternal ();
490 IEnumerator<KeyValuePair<TKey, TValue>> GetEnumeratorInternal ()
492 foreach (Basket b in container) {
494 foreach (Pair p in b)
495 yield return new KeyValuePair<TKey, TValue> (p.Key, p.Value);
500 IDictionaryEnumerator IDictionary.GetEnumerator ()
502 return new ConcurrentDictionaryEnumerator (GetEnumeratorInternal ());
505 class ConcurrentDictionaryEnumerator : IDictionaryEnumerator
507 IEnumerator<KeyValuePair<TKey, TValue>> internalEnum;
509 public ConcurrentDictionaryEnumerator (IEnumerator<KeyValuePair<TKey, TValue>> internalEnum)
511 this.internalEnum = internalEnum;
514 public bool MoveNext ()
516 return internalEnum.MoveNext ();
521 internalEnum.Reset ();
524 public object Current {
530 public DictionaryEntry Entry {
532 KeyValuePair<TKey, TValue> current = internalEnum.Current;
533 return new DictionaryEntry (current.Key, current.Value);
539 return internalEnum.Current.Key;
543 public object Value {
545 return internalEnum.Current.Value;
550 object ICollection.SyncRoot {
557 bool IDictionary.IsFixedSize {
563 bool ICollection.IsSynchronized {
567 bool TryGetBasket (TKey key, out Basket basket)
570 if (!container.GetFromHash (key.GetHashCode (), out basket))