Using a thread-safe dictionary in .NET C# Part 4: thread-safe insertlookups
August 4, 2015 Leave a comment
In the previous post we looked at how the AddOrUpdate method worked. We saw that it was a very neat and thread-safe way to either insert a new key-value pair or update an existing one.
We’ve already seen a thread-safe method that helps you retrieve values by their keys in this post: TryGet. It returns true if the item could be retrieved. TryAdd on the other hand is used to insert a new key-value pair. It also returns true if the item could be inserted successfully. However, what do you do with the returned boolean values? Do you keep trying in some loop until it succeeds?
The GetOrAdd method combines the element retrieval and insertion in one operation. It accepts a key and a value and returns the value of the key-value pair. As its name implies it adds the provided key-value pair if there’s no element with that value. Also, it updates an existing element if it already exists by a given key. Alternatively it accepts a delegate – a lambda function – which calculates the value based on the key. That function is similar to the one we saw in the previous post on AddOrUpdate method which also accepted a delegate.
The AddOrGet delegate differs from the AddOrUpdate delegate in that its signature only accepts the key for a newly inserted key-value pair.
If there’s already a key-value pair with that key then the update parameter is ignored.
The following example demonstrates the GetOrAdd method in action:
public void RunGetOrAddSample() { ConcurrentDictionary<string, int> movieCategoriesOnStock = new ConcurrentDictionary<string, int>(); movieCategoriesOnStock.TryAdd("Romance", 12); movieCategoriesOnStock.TryAdd("Action", 9); movieCategoriesOnStock.TryAdd("Comedy", 20); Debug.WriteLine("Dictionary content before AddOrGet:"); foreach (var kvp in movieCategoriesOnStock) { Debug.WriteLine(string.Format("{0}: {1}", kvp.Key, kvp.Value)); } int zombie = movieCategoriesOnStock.GetOrAdd("Zombie", 10); Debug.WriteLine("Zombie: {0}", zombie); int action = movieCategoriesOnStock.GetOrAdd("Action", 234); Debug.WriteLine("Action: {0}", action); int scifi = movieCategoriesOnStock.GetOrAdd("Sci-fi", (key) => { return key.Length; }); Debug.WriteLine("Sci-fi: {0}", scifi); int drama = movieCategoriesOnStock.GetOrAdd("Drama", (key) => { return CalculateNewValueFromKey(key); }); Debug.WriteLine("Drama: {0}", drama); Debug.WriteLine("Dictionary content after GetOrAdd:"); foreach (var kvp in movieCategoriesOnStock) { Debug.WriteLine(string.Format("{0}: {1}", kvp.Key, kvp.Value)); } } private int CalculateNewValueFromKey(string key) { return key.GetHashCode(); }
…and it gives the following output:
Dictionary content before AddOrGet:
Action: 9
Romance: 12
Comedy: 20
Zombie: 10
Action: 9
Sci-fi: 6
Drama: -1538741386
Dictionary content after GetOrAdd:
Action: 9
Romance: 12
Drama: -1538741386
Zombie: 10
Sci-fi: 6
Comedy: 20
View the list of posts on the Task Parallel Library here.