/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; namespace QuantConnect.Util { /// /// Provides a thread-safe set collection that mimics the behavior of /// and will be keep insertion order /// /// The item type public class ConcurrentSet : ISet { private readonly IEnumerator _emptyEnumerator = Enumerable.Empty().GetEnumerator(); private readonly OrderedDictionary _set = new OrderedDictionary(); // for performance we will keep a enumerator list which we will refresh only if required private readonly List _enumerator = new List(); private bool _refreshEnumerator; /// Gets the number of elements contained in the . /// The number of elements contained in the . public int Count { get { lock (_set) { return _set.Count; } } } /// Gets a value indicating whether the is read-only. /// true if the is read-only; otherwise, false. public bool IsReadOnly => false; /// Adds an item to the . /// The object to add to the . /// The is read-only. void ICollection.Add(T item) { if (item == null) { throw new ArgumentNullException(nameof(item)); } lock (_set) { AddImpl(item); } } /// Modifies the current set so that it contains all elements that are present in either the current set or the specified collection. /// The collection to compare to the current set. /// /// is null. public void UnionWith(IEnumerable other) { var otherSet = other.ToHashSet(); lock (_set) { foreach (var item in otherSet) { // wont overwrite existing references of same key AddImpl(item); } } } /// Modifies the current set so that it contains only elements that are also in a specified collection. /// The collection to compare to the current set. /// /// is null. public void IntersectWith(IEnumerable other) { var otherSet = other.ToHashSet(); lock (_set) { // remove items in '_set' that are not in 'other' // enumerating this and not '_set' so its safe to modify foreach (var item in this) { if (!otherSet.Contains(item)) { RemoveImpl(item); } } } } /// Removes all elements in the specified collection from the current set. /// The collection of items to remove from the set. /// /// is null. public void ExceptWith(IEnumerable other) { var otherSet = other.ToHashSet(); lock (_set) { // remove items from 'other' foreach (var item in otherSet) { RemoveImpl(item); } } } /// Modifies the current set so that it contains only elements that are present either in the current set or in the specified collection, but not both. /// The collection to compare to the current set. /// /// is null. public void SymmetricExceptWith(IEnumerable other) { var otherSet = other.ToHashSet(); lock (_set) { foreach (var item in otherSet) { if (!AddImpl(item)) { // remove items in both collections RemoveImpl(item); } } } } /// Determines whether a set is a subset of a specified collection. /// true if the current set is a subset of ; otherwise, false. /// The collection to compare to the current set. /// /// is null. public bool IsSubsetOf(IEnumerable other) { var otherSet = other.ToHashSet(); lock (_set) { foreach (var item in otherSet) { if (!_set.Contains(item)) { return false; } } // non-strict subset can be equal return _set.Keys.Count == 0; } } /// Determines whether the current set is a superset of a specified collection. /// true if the current set is a superset of ; otherwise, false. /// The collection to compare to the current set. /// /// is null. public bool IsSupersetOf(IEnumerable other) { var otherSet = other.ToHashSet(); lock (_set) { foreach (DictionaryEntry item in _set) { if (!otherSet.Remove((T)item.Key)) { return false; } } } // non-strict superset can be equal return true; } /// Determines whether the current set is a proper (strict) superset of a specified collection. /// true if the current set is a proper superset of ; otherwise, false. /// The collection to compare to the current set. /// /// is null. public bool IsProperSupersetOf(IEnumerable other) { var hasOther = false; var otherSet = other.ToHashSet(); lock (_set) { foreach (DictionaryEntry item in _set) { if (!otherSet.Remove((T)item.Key)) { return false; } hasOther = true; } } // to be a strict superset, _set must contain extra elements and contain all of other return hasOther; } /// Determines whether the current set is a proper (strict) subset of a specified collection. /// true if the current set is a proper subset of ; otherwise, false. /// The collection to compare to the current set. /// /// is null. public bool IsProperSubsetOf(IEnumerable other) { var hasOther = false; var otherSet = other.ToHashSet(); lock (_set) { foreach (var item in otherSet) { if (!_set.Contains(item)) { return false; } hasOther = true; } // to be a strict subset, other must contain extra elements and _set must contain all of other return hasOther && _set.Keys.Count == 0; } } /// Determines whether the current set overlaps with the specified collection. /// true if the current set and share at least one common element; otherwise, false. /// The collection to compare to the current set. /// /// is null. public bool Overlaps(IEnumerable other) { var otherSet = other.ToHashSet(); lock (_set) { foreach (var item in otherSet) { if (_set.Contains(item)) { return true; } } } return false; } /// Determines whether the current set and the specified collection contain the same elements. /// true if the current set is equal to ; otherwise, false. /// The collection to compare to the current set. /// /// is null. public bool SetEquals(IEnumerable other) { var otherSet = other.ToHashSet(); lock (_set) { foreach (DictionaryEntry item in _set) { if (!otherSet.Remove((T) item.Key)) { return false; } } } return otherSet.Count == 0; } /// Adds an element to the current set and returns a value to indicate if the element was successfully added. /// true if the element is added to the set; false if the element is already in the set. /// The element to add to the set. public bool Add(T item) { lock (_set) { return AddImpl(item); } } /// Removes all items from the . /// The is read-only. public void Clear() { lock (_set) { _refreshEnumerator = true; _set.Clear(); } } /// Determines whether the contains a specific value. /// true if is found in the ; otherwise, false. /// The object to locate in the . public bool Contains(T item) { lock (_set) { return item != null && _set.Contains(item); } } /// Copies the elements of the to an , starting at a particular index. /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. /// The zero-based index in at which copying begins. /// /// is null. /// /// is less than 0. /// The number of elements in the source is greater than the available space from to the end of the destination . public void CopyTo(T[] array, int arrayIndex) { lock (_set) { foreach (DictionaryEntry item in _set) { array[arrayIndex++] = (T) item.Key; } } } /// Removes the first occurrence of a specific object from the . /// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . /// The object to remove from the . /// The is read-only. public bool Remove(T item) { lock (_set) { if (item != null && _set.Contains(item)) { RemoveImpl(item); return true; } return false; } } /// Returns an enumerator that iterates through the collection. /// For thread safety will return a snapshot of the collection /// A that can be used to iterate through the collection. /// 1 public IEnumerator GetEnumerator() { lock (_set) { if (_refreshEnumerator) { _enumerator.Clear(); foreach (DictionaryEntry item in _set) { _enumerator.Add((T)item.Key); } _refreshEnumerator = false; } return _enumerator.Count == 0 ? _emptyEnumerator : _enumerator.GetEnumerator(); } } /// Returns an enumerator that iterates through a collection. /// For thread safety will return a snapshot of the collection /// An object that can be used to iterate through the collection. /// 2 IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private bool AddImpl(T item) { if (!_set.Contains(item)) { _refreshEnumerator = true; _set.Add(item, item); return true; } return false; } private void RemoveImpl(T item) { _refreshEnumerator = true; _set.Remove(item); } } }