// // Copyright (c) 2010-2018 Antmicro // Copyright (c) 2011-2015 Realtime Embedded // // This file is licensed under the MIT License. // Full license text is available in 'licenses/MIT.txt'. // using System; using System.Collections.Generic; using System.Linq; namespace Antmicro.Renode.Utilities.Collections { /// /// This is an implementation of MultiValueDictionary which is memory effictient at expense of sligthly /// more logic. It holds key-value associations for singular values in plain dictionary, and moves them to /// a key-list of values dictionary when more than one value gets associated to the same key. /// /// This way, the overhead of keeping lists is limited only to keys that actually need it. /// /// Semantically it behaves as Dictionary>. So Get methods /// always return a collection, even in cases when there are no elements or one element to return. /// public class MultiValueDictionary : IEnumerable where TValue : IEquatable { public MultiValueDictionary() { multiple = new Dictionary>(); single = new Dictionary(); } public IEnumerator GetEnumerator() { return single.Values.Concat(multiple.Values.SelectMany(list => list)).GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } public void Add(TKey key, TValue value) { TValue existingSingle; if(single.TryGetValue(key, out existingSingle)) { single.Remove(key); multiple.Add(key, new List{ existingSingle, value }); return; } List existingMultiple; if(multiple.TryGetValue(key, out existingMultiple)) { existingMultiple.Add(value); return; } single.Add(key, value); } public void AddRange(TKey key, IEnumerable values) { TValue existingSingle; if(single.TryGetValue(key, out existingSingle)) { single.Remove(key); var list = new List{ existingSingle }; list.AddRange(values); multiple.Add(key, list); return; } List existingMultiple; if(multiple.TryGetValue(key, out existingMultiple)) { existingMultiple.AddRange(values); } else { multiple.Add(key, new List(values)); } } /// /// Removes all values associated with the key. /// /// Key. public bool Remove(TKey key) { return single.Remove(key) || multiple.Remove(key); } /// /// Removes particular key, value association. /// /// Key. /// Value. public bool Remove(TKey key, TValue value) { TValue existingSingle; if(single.TryGetValue(key, out existingSingle) && existingSingle.Equals(value)) { return single.Remove(key); } List existingMultipleValues; if(multiple.TryGetValue(key, out existingMultipleValues) && existingMultipleValues.Remove(value)) { //if there is one element left in list, move it to the single values if(existingMultipleValues.Count == 1) { single.Add(key, existingMultipleValues.First()); multiple.Remove(key); } } return true; } /// /// Tries to get the collection of values associated with the key. /// /// true, if collection is not empty, false otherwise. /// Key. /// Values. public bool TryGetValue(TKey key, out IReadOnlyCollection values) { TValue existingSingle; if(single.TryGetValue(key, out existingSingle)) { values = new List { existingSingle }; return true; } List existingMultiple; var ret = multiple.TryGetValue(key, out existingMultiple); if(ret) { values = existingMultiple.AsReadOnly(); } else { values = new List(); } return ret; } public bool ContainsKey(TKey key) { return single.ContainsKey(key) || multiple.ContainsKey(key); } public bool ContainsValue(TValue value) { return this.Contains(value); } public bool Contains(TKey key) { return ContainsKey(key); } public bool Contains(TKey key, TValue value) { IReadOnlyCollection values; return TryGetValue(key, out values) && values.Contains(value); } public void Clear() { single.Clear(); multiple.Clear(); } /// /// Contains dictionary of keys associated with one value. /// private readonly Dictionary single; /// /// Contains dictionary of keys associated with more than one value. /// private readonly Dictionary> multiple; } }