/*
* 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);
}
}
}