How is a C# dictionary implemented? (C++) [closed] - c#

As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 10 years ago.
Hi I'm using C++ to create a C# like dictionary object. I use a similar system of garbage collected ref objects for everything which are moved around in memory as the program runs. I've started off by implementing the dictionary aa a pretty standard hash table which is fine and I have this type of layout:
header + hash table -> storage -> element 0 -> object x
element 1 -> object y
element ... -> object ...
'+': In same allocation
'->': Indirect pointer ie different allocation
So 'header' contains just the table size. 'Hash table' is an array of integer offsets into the storage area.
The storage is implemented as a C# list ie an indirect pointer (ref object) to a self sizing array ie like a C++ vector.
Each element (Dictionary::Element) in the storage holds an id, an indirect pointer (ref object) to the actual object and an integer offset to the next element.
// C++ like pseudo code:
template< typename _Type_, typename _HashType_ = int >
class Dictionary
{
private:
class Element
{
_HashType_ m_id;
_Type_ m_object; // Ref object ie indirect pointer to actual object
int m_next; // Next element
}
int m_tablesize; // Power of 2
int* m_table; // Pointer here but in reality just a continuous block
// of memory after m_tablesize;
List<Element> m_storage; // Like a C++ vector
}
So my question is C#'s dictionary only allows one object at a time for any one hash.
Is there a simpler approach than the above implementation?
For example Dictonary::Add(_HashType_ id, _Type_ object) in the above implementation will bitwise AND the hash with the table size to get an index into the hash table then allocate an element with the id and object passed in and then it will add (push back) that element to the list (m_storage) of elements and fix up the linked list of elements:
template < typename _Type_, typename _HashType_ >
inline bool Dictionary< _Type_, _HashType_ >::Add( _HashType_ id, _Type_ element )
{
Element element = Element::New( id, object );
m_storage->Add( element );
// PushBack here fixes up the offset of the element in the storage array stored in
// the hash table (zero elements for this id) or the next pointer in the element
// (one or more elements exist for this id)
return PushBack( element );
}
Being a bit more explicit: is there a way to just have a header and hash table of objects ie:
header + hash table -> object x
object y
...
I ask this because C# imposes a one item limit on each hash when the more complex implementation above has no such limitations really except Remove would need to pass in both the id and the object and possibly you might want PushFront and PushBack instead of Add.
Thanks in advance and please don't ask why I'm doing this seemingly crazy thing just humor me! :)

You can use a decompiler to see how the dictionary is implemented in mscorlib. It is too long to include, but here is a snippet:
namespace System.Collections.Generic {
using System;
using System.Collections;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Runtime.Serialization;
using System.Security.Permissions;
[DebuggerTypeProxy(typeof(Mscorlib_DictionaryDebugView<,>))]
[DebuggerDisplay("Count = {Count}")]
[Serializable]
[System.Runtime.InteropServices.ComVisible(false)]
public class Dictionary<TKey,TValue>: IDictionary<TKey,TValue>, IDictionary, IReadOnlyDictionary<TKey, TValue>, ISerializable, IDeserializationCallback {
private struct Entry {
public int hashCode; // Lower 31 bits of hash code, -1 if unused
public int next; // Index of next entry, -1 if last
public TKey key; // Key of entry
public TValue value; // Value of entry
}
private int[] buckets;
private Entry[] entries;
private int count;
private int version;
private int freeList;
private int freeCount;
private IEqualityComparer<TKey> comparer;
private KeyCollection keys;
private ValueCollection values;
private Object _syncRoot;
// constants for serialization
private const String VersionName = "Version";
private const String HashSizeName = "HashSize"; // Must save buckets.Length
private const String KeyValuePairsName = "KeyValuePairs";
private const String ComparerName = "Comparer";
public Dictionary(): this(0, null) {}
public Dictionary(int capacity): this(capacity, null) {}
public Dictionary(IEqualityComparer<TKey> comparer): this(0, comparer) {}
public Dictionary(int capacity, IEqualityComparer<TKey> comparer) {
if (capacity < 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity);
if (capacity > 0) Initialize(capacity);
this.comparer = comparer ?? EqualityComparer<TKey>.Default;
}
public Dictionary(IDictionary<TKey,TValue> dictionary): this(dictionary, null) {}
public Dictionary(IDictionary<TKey,TValue> dictionary, IEqualityComparer<TKey> comparer):
this(dictionary != null? dictionary.Count: 0, comparer) {
if( dictionary == null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary);
}
foreach (KeyValuePair<TKey,TValue> pair in dictionary) {
Add(pair.Key, pair.Value);
}
}
protected Dictionary(SerializationInfo info, StreamingContext context) {
//We can't do anything with the keys and values until the entire graph has been deserialized
//and we have a resonable estimate that GetHashCode is not going to fail. For the time being,
//we'll just cache this. The graph is not valid until OnDeserialization has been called.
HashHelpers.SerializationInfoTable.Add(this, info);
}
public IEqualityComparer<TKey> Comparer {
get {
return comparer;
}
}
public int Count {
get { return count - freeCount; }
}
public KeyCollection Keys {
get {
Contract.Ensures(Contract.Result<KeyCollection>() != null);
if (keys == null) keys = new KeyCollection(this);
return keys;
}
}
ICollection<TKey> IDictionary<TKey, TValue>.Keys {
get {
if (keys == null) keys = new KeyCollection(this);
return keys;
}
}
IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys {
get {
if (keys == null) keys = new KeyCollection(this);
return keys;
}
}
public ValueCollection Values {
get {
Contract.Ensures(Contract.Result<ValueCollection>() != null);
if (values == null) values = new ValueCollection(this);
return values;
}
}
ICollection<TValue> IDictionary<TKey, TValue>.Values {
get {
if (values == null) values = new ValueCollection(this);
return values;
}
}
IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values {
get {
if (values == null) values = new ValueCollection(this);
return values;
}
}
public TValue this[TKey key] {
get {
int i = FindEntry(key);
if (i >= 0) return entries[i].value;
ThrowHelper.ThrowKeyNotFoundException();
return default(TValue);
}
set {
Insert(key, value, false);
}
}
public void Add(TKey key, TValue value) {
Insert(key, value, true);
}
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> keyValuePair) {
Add(keyValuePair.Key, keyValuePair.Value);
}
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> keyValuePair) {
int i = FindEntry(keyValuePair.Key);
if( i >= 0 && EqualityComparer<TValue>.Default.Equals(entries[i].value, keyValuePair.Value)) {
return true;
}
return false;
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> keyValuePair) {
int i = FindEntry(keyValuePair.Key);
if( i >= 0 && EqualityComparer<TValue>.Default.Equals(entries[i].value, keyValuePair.Value)) {
Remove(keyValuePair.Key);
return true;
}
return false;
}
public void Clear() {
if (count > 0) {
for (int i = 0; i < buckets.Length; i++) buckets[i] = -1;
Array.Clear(entries, 0, count);
freeList = -1;
count = 0;
freeCount = 0;
version++;
}
}
public bool ContainsKey(TKey key) {
return FindEntry(key) >= 0;
}
public bool ContainsValue(TValue value) {
if (value == null) {
for (int i = 0; i < count; i++) {
if (entries[i].hashCode >= 0 && entries[i].value == null) return true;
}
}
else {
EqualityComparer<TValue> c = EqualityComparer<TValue>.Default;
for (int i = 0; i < count; i++) {
if (entries[i].hashCode >= 0 && c.Equals(entries[i].value, value)) return true;
}
}
return false;
}
private void CopyTo(KeyValuePair<TKey,TValue>[] array, int index) {
if (array == null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
}
if (index < 0 || index > array.Length ) {
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
}
if (array.Length - index < Count) {
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
}
int count = this.count;
Entry[] entries = this.entries;
for (int i = 0; i < count; i++) {
if (entries[i].hashCode >= 0) {
array[index++] = new KeyValuePair<TKey,TValue>(entries[i].key, entries[i].value);
}
}
}
public Enumerator GetEnumerator() {
return new Enumerator(this, Enumerator.KeyValuePair);
}
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() {
return new Enumerator(this, Enumerator.KeyValuePair);
}
[System.Security.SecurityCritical] // auto-generated_required
public virtual void GetObjectData(SerializationInfo info, StreamingContext context) {
if (info==null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info);
}
info.AddValue(VersionName, version);
#if FEATURE_RANDOMIZED_STRING_HASHING
info.AddValue(ComparerName, HashHelpers.GetEqualityComparerForSerialization(comparer), typeof(IEqualityComparer<TKey>));
#else
info.AddValue(ComparerName, comparer, typeof(IEqualityComparer<TKey>));
#endif
info.AddValue(HashSizeName, buckets == null ? 0 : buckets.Length); //This is the length of the bucket array.
if( buckets != null) {
KeyValuePair<TKey, TValue>[] array = new KeyValuePair<TKey, TValue>[Count];
CopyTo(array, 0);
info.AddValue(KeyValuePairsName, array, typeof(KeyValuePair<TKey, TValue>[]));
}
}
private int FindEntry(TKey key) {
if( key == null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
}
if (buckets != null) {
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
for (int i = buckets[hashCode % buckets.Length]; i >= 0; i = entries[i].next) {
if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) return i;
}
}
return -1;
}
private void Initialize(int capacity) {
int size = HashHelpers.GetPrime(capacity);
buckets = new int[size];
for (int i = 0; i < buckets.Length; i++) buckets[i] = -1;
entries = new Entry[size];
freeList = -1;
}
private void Insert(TKey key, TValue value, bool add) {
if( key == null ) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
}
if (buckets == null) Initialize(0);
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
int targetBucket = hashCode % buckets.Length;
#if FEATURE_RANDOMIZED_STRING_HASHING
int collisionCount = 0;
#endif
for (int i = buckets[targetBucket]; i >= 0; i = entries[i].next) {
if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) {
if (add) {
ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AddingDuplicate);
}
entries[i].value = value;
version++;
return;
}
#if FEATURE_RANDOMIZED_STRING_HASHING
collisionCount++;
#endif
}
int index;
if (freeCount > 0) {
index = freeList;
freeList = entries[index].next;
freeCount--;
}
else {
if (count == entries.Length)
{
Resize();
targetBucket = hashCode % buckets.Length;
}
index = count;
count++;
}
entries[index].hashCode = hashCode;
entries[index].next = buckets[targetBucket];
entries[index].key = key;
entries[index].value = value;
buckets[targetBucket] = index;
version++;
#if FEATURE_RANDOMIZED_STRING_HASHING
if(collisionCount > HashHelpers.HashCollisionThreshold && HashHelpers.IsWellKnownEqualityComparer(comparer))
{
comparer = (IEqualityComparer<TKey>) HashHelpers.GetRandomizedEqualityComparer(comparer);
Resize(entries.Length, true);
}
#endif
}
public virtual void OnDeserialization(Object sender) {
SerializationInfo siInfo;
HashHelpers.SerializationInfoTable.TryGetValue(this, out siInfo);
if (siInfo==null) {
// It might be necessary to call OnDeserialization from a container if the container object also implements
// OnDeserialization. However, remoting will call OnDeserialization again.
// We can return immediately if this function is called twice.
// Note we set remove the serialization info from the table at the end of this method.
return;
}
int realVersion = siInfo.GetInt32(VersionName);
int hashsize = siInfo.GetInt32(HashSizeName);
comparer = (IEqualityComparer<TKey>)siInfo.GetValue(ComparerName, typeof(IEqualityComparer<TKey>));
if( hashsize != 0) {
buckets = new int[hashsize];
for (int i = 0; i < buckets.Length; i++) buckets[i] = -1;
entries = new Entry[hashsize];
freeList = -1;
KeyValuePair<TKey, TValue>[] array = (KeyValuePair<TKey, TValue>[])
siInfo.GetValue(KeyValuePairsName, typeof(KeyValuePair<TKey, TValue>[]));
if (array==null) {
ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MissingKeys);
}
for (int i=0; i<array.Length; i++) {
if ( array[i].Key == null) {
ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_NullKey);
}
Insert(array[i].Key, array[i].Value, true);
}
}
else {
buckets = null;
}
version = realVersion;
HashHelpers.SerializationInfoTable.Remove(this);
}
private void Resize() {
Resize(HashHelpers.ExpandPrime(count), false);
}
private void Resize(int newSize, bool forceNewHashCodes) {
Contract.Assert(newSize >= entries.Length);
int[] newBuckets = new int[newSize];
for (int i = 0; i < newBuckets.Length; i++) newBuckets[i] = -1;
Entry[] newEntries = new Entry[newSize];
Array.Copy(entries, 0, newEntries, 0, count);
if(forceNewHashCodes) {
for (int i = 0; i < count; i++) {
if(newEntries[i].hashCode != -1) {
newEntries[i].hashCode = (comparer.GetHashCode(newEntries[i].key) & 0x7FFFFFFF);
}
}
}
for (int i = 0; i < count; i++) {
int bucket = newEntries[i].hashCode % newSize;
newEntries[i].next = newBuckets[bucket];
newBuckets[bucket] = i;
}
buckets = newBuckets;
entries = newEntries;
}
public bool Remove(TKey key) {
if(key == null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
}
if (buckets != null) {
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
int bucket = hashCode % buckets.Length;
int last = -1;
for (int i = buckets[bucket]; i >= 0; last = i, i = entries[i].next) {
if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) {
if (last < 0) {
buckets[bucket] = entries[i].next;
}
else {
entries[last].next = entries[i].next;
}
entries[i].hashCode = -1;
entries[i].next = freeList;
entries[i].key = default(TKey);
entries[i].value = default(TValue);
freeList = i;
freeCount++;
version++;
return true;
}
}
}
return false;
}
public bool TryGetValue(TKey key, out TValue value) {
int i = FindEntry(key);
if (i >= 0) {
value = entries[i].value;
return true;
}
value = default(TValue);
return false;
}
// This is a convenience method for the internal callers that were converted from using Hashtable.
// Many were combining key doesn't exist and key exists but null value (for non-value types) checks.
// This allows them to continue getting that behavior with minimal code delta. This is basically
// TryGetValue without the out param
internal TValue GetValueOrDefault(TKey key) {
int i = FindEntry(key);
if (i >= 0) {
return entries[i].value;
}
return default(TValue);
}
bool ICollection<KeyValuePair<TKey,TValue>>.IsReadOnly {
get { return false; }
}
void ICollection<KeyValuePair<TKey,TValue>>.CopyTo(KeyValuePair<TKey,TValue>[] array, int index) {
CopyTo(array, index);
}
void ICollection.CopyTo(Array array, int index) {
if (array == null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
}
if (array.Rank != 1) {
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported);
}
if( array.GetLowerBound(0) != 0 ) {
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound);
}
if (index < 0 || index > array.Length) {
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
}
if (array.Length - index < Count) {
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
}
KeyValuePair<TKey,TValue>[] pairs = array as KeyValuePair<TKey,TValue>[];
if (pairs != null) {
CopyTo(pairs, index);
}
else if( array is DictionaryEntry[]) {
DictionaryEntry[] dictEntryArray = array as DictionaryEntry[];
Entry[] entries = this.entries;
for (int i = 0; i < count; i++) {
if (entries[i].hashCode >= 0) {
dictEntryArray[index++] = new DictionaryEntry(entries[i].key, entries[i].value);
}
}
}
else {
object[] objects = array as object[];
if (objects == null) {
ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
}
try {
int count = this.count;
Entry[] entries = this.entries;
for (int i = 0; i < count; i++) {
if (entries[i].hashCode >= 0) {
objects[index++] = new KeyValuePair<TKey,TValue>(entries[i].key, entries[i].value);
}
}
}
catch(ArrayTypeMismatchException) {
ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
}
}
}
IEnumerator IEnumerable.GetEnumerator() {
return new Enumerator(this, Enumerator.KeyValuePair);
}
bool ICollection.IsSynchronized {
get { return false; }
}
object ICollection.SyncRoot {
get {
if( _syncRoot == null) {
System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null);
}
return _syncRoot;
}
}
bool IDictionary.IsFixedSize {
get { return false; }
}
bool IDictionary.IsReadOnly {
get { return false; }
}
< The rest of the code ommited...>

Its pretty similar to a std::map though a std::map doesn't require you to add an entry to it first ... you can add by just using the map's entry
ie.
std::map< int, int > m;
m[78]++;
will create and increment the int value represented by the key "78" ...

Related

Output binary tree (inorder traversal iterative) doesn't work

I need to output tree. But this output is incorrect.
My tree is:
`
public class ElTree
{
public int L;
public string inf;
public bool PR;
public ElTree(string inf)
{
this.inf = inf;
this.PR = false;
this.L = -1;
}
}
`
Class Tree is (Here other methods that I do with Tree, the method that output Tree in in the end. I`ve also tried to do output with recursion and it work correctly, so method Add() works correctly. But I have a task to do output in iterative way). For example, when I add tree lake "k"-root, "r"-right elem, "l"-left elem then my recursive method output "lkr" but iterative method output just "ll":
`
public class Tree
{
public ElTree[] T;
public int Coun { get { return _count; } }
private int _count;
public Tree(string inf)
{
ElTree temp = new ElTree(inf);
T = new ElTree[1];
T[T.Length-1] = temp;
}
public int Find(int V, string inf)
{
if (V == T.Length) return -1;
if (V != -1)
{
if (T[V].inf == inf) return V;
int temp = Find(T[V].L, inf);
if (temp != -1)
return temp;
return temp = Find(V + 1, inf);
}
return -1;
}
public bool Empty()
{
if (Coun == 0)
{
return true;
}
else
return false;
}
public bool Add(int i, string inf, char s)
{
ElTree temp = new ElTree(inf);
switch (s)
{
case 'L':
if (T[i].L == -1)
{
Array.Resize(ref T, T.Length + 1);
T[T.Length - 1] = temp;
T[i].L = T.Length - 1;
_count++;
return true;
}
else return false;
case 'R':
if (!T[i].PR)
{
Array.Resize(ref T, T.Length + 1);
T[T.Length - 1] = temp;
T[i].PR = true;
_count++;
return true;
}
else return false;
}
return false;
}
public void TreeInStringit(int i, ref string s)//iterative output
{
Stack<int> stack = new Stack<int>();
//int i = 0;
stack.Push(i);
do
{
while (T[i].L != -1 && i < T.Length)
{
i = T[i].L;
stack.Push(i);
}
s += T[i].inf;
while (T[i].PR && i < T.Length)
{
i++;
stack.Push(i);
}
if (stack.Count != 0) stack.Pop();
} while (stack.Count != 0);
}
public void String(int V, ref string s)//recursive output
{
ElTree temp = T[V];
if (temp.L != -1)
String(temp.L, ref s);
s += temp.inf;
if (temp.PR)
String(V+1, ref s);
}
`
When I add tree like root with two elements (one on left side and one on right) it output left side of tree 2 times. But I need output all tree.

Sorted data structure

I am looking for a sorted keyed data structure in .Net 4.0 supporting the following functionality:
Create the structure in O(n log n) time
Get the item by key in O(log n) time
Find the smallest item in the collection greater or equal to a given argument in O(log n) time (we will key it using double most probably)
Find the biggest item smaller than a given argument in O(log n)
For a given item in the collection, get the next and previous item
Keys need to be unique in the collection
I took a quick look at the SortedDictionary and SortedList, but they don't seem to provide (3) and (4) from the list above. SortedDictionary doesn't seem to support (5),
and I am not sure if SortedList supports (6).
We are limited to .Net4 unfortunately.
You're going to need to write your own collection. Conceptually what you want appears to be a tree based structure, which is how SortedDictionary is implemented. The underlying structure has the potential for all of these tasks, the .NET implementation simply doesn't expose them all, nor does it provide access to enough of the underlying tools to accomplish those goals, forcing you to start from scratch.
Fortunately, building such tree based structures is a common task for introductory programmers, so as a result you'll find plenty of open source 3rd party implementations to look through, either that will accomplish your goals, or as a starting place. You could also consider grabbing the source of SortedDictionary and re-compiling your own version, if you want.
I've created a sorted dictionary for you. I hope it meets your needs.
public class MyDictionary<TKey, TItem> : IDictionary<TKey, TItem>
where TKey : IComparable<TKey>
where TItem : IEquatable<TItem>
{
private readonly List<TKey> keys;
private readonly List<TItem> items;
private readonly ReadOnlyCollection<TKey> roKeys;
private readonly ReadOnlyCollection<TItem> roItems;
public MyDictionary()
{
keys = new List<TKey>();
items = new List<TItem>();
roKeys = new ReadOnlyCollection<TKey>(keys);
roItems = new ReadOnlyCollection<TItem>(items);
}
public MyDictionary(int capacity)
{
keys = new List<TKey>(capacity);
items = new List<TItem>(capacity);
roKeys = new ReadOnlyCollection<TKey>(keys);
roItems = new ReadOnlyCollection<TItem>(items);
}
public MyDictionary(TKey[] keys, TItem[] items)
{
if (keys == null)
throw new ArgumentNullException("keys");
if (items == null)
throw new ArgumentNullException("items");
if (keys.Length != items.Length)
throw new ArgumentException("Arrays lengths must be equal.");
TKey[] keysCopy = new TKey[keys.Length];
keys.CopyTo(keysCopy, 0);
TItem[] itemsCopy = new TItem[items.Length];
items.CopyTo(itemsCopy, 0);
Array.Sort(keysCopy, itemsCopy);
this.keys = new List<TKey>(keysCopy);
this.items = new List<TItem>(itemsCopy);
roKeys = new ReadOnlyCollection<TKey>(keys);
roItems = new ReadOnlyCollection<TItem>(items);
}
public int BinarySearch(TKey key)
{
return keys.BinarySearch(key);
}
public bool ContainsKey(TKey key)
{
return BinarySearch(key) >= 0;
}
public void Add(TKey key, TItem item)
{
int index = BinarySearch(key);
if (index >= 0)
throw new ArgumentException(String.Format("The key {0} already exists.", key), "key");
index = ~index;
keys.Insert(index, key);
items.Insert(index, item);
}
public void Add(KeyValuePair<TKey, TItem> item)
{
Add(item.Key, item.Value);
}
public bool Remove(TKey key)
{
int index = BinarySearch(key);
if (index < 0)
return false;
keys.RemoveAt(index);
items.RemoveAt(index);
return true;
}
public bool Remove(KeyValuePair<TKey, TItem> item)
{
int index = BinarySearch(item.Key);
if (index < 0)
return false;
index = ~index;
keys.RemoveAt(index);
items.RemoveAt(index);
return true;
}
public bool Contains(KeyValuePair<TKey, TItem> item)
{
int index = BinarySearch(item.Key);
if (index < 0)
return false;
index = ~index;
return items[index].Equals(item.Value);
}
public bool TryGetValue(TKey key, out TItem value)
{
int index = BinarySearch(key);
if (index < 0)
{
value = default(TItem);
return false;
}
value = items[index];
return true;
}
public TItem this[TKey key]
{
get
{
int index = BinarySearch(key);
if (index < 0)
throw new ArgumentException(String.Format("The key {0} not found.", key), "key");
return items[index];
}
set
{
int index = BinarySearch(key);
if (index < 0)
throw new ArgumentException(String.Format("The key {0} not found.", key), "key");
items[index] = value;
}
}
public ICollection<TKey> Keys
{
get { return roKeys; }
}
public ICollection<TItem> Values
{
get { return roItems; }
}
public IEnumerator<KeyValuePair<TKey, TItem>> GetEnumerator()
{
return keys.Select((t, i) => new KeyValuePair<TKey, TItem>(t, items[i])).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Clear()
{
keys.Clear();
items.Clear();
}
public void CopyTo(KeyValuePair<TKey, TItem>[] array, int arrayIndex)
{
Array.Copy(keys.Select((t, i) => new KeyValuePair<TKey, TItem>(t, items[i])).ToArray(), 0, array, arrayIndex, Count);
}
public int Count
{
get { return keys.Count; }
}
public int Capacity
{
get { return keys.Capacity; }
set
{
if (value < 0)
throw new ArgumentOutOfRangeException("value");
keys.Capacity = value;
items.Capacity = value;
}
}
public bool IsReadOnly
{
get { return false; }
}
public int GetSmallerOrEqualIndex(TKey key)
{
int index = BinarySearch(key);
if (index >= 0)
return index;
index = ~index;
return index - 1;
}
public int GetGreaterOrEqualIndex(TKey key)
{
int index = BinarySearch(key);
if (index >= 0)
return index;
index = ~index;
return index;
}
public KeyValuePair<TKey, TItem> GetItem(int index)
{
return new KeyValuePair<TKey, TItem>(keys[index], items[index]);
}
}
The requirements:
NOT Satisfied (I'm working on it). At the moment, initializing via MyDictionary(TKey[] keys, TItem[] items) constructor is on average an O(n log n) operation, in the worst case it is an O(n ^ 2) operation. Adding an individual item is an O(n) operation.
Satisfied.
Satisfied (GetGreaterOrEqualIndex method).
Satisfied (GetSmallerOrEqualIndex method).
Not satisfied directly, but satisfied for the item's index, if I understood "given item" correctly.
Satisfied.

How to create dynamic incrementing variable using “for” loop in C#

How to create dynamic incrementing variable using "for" loop in C#? like this:
track_1, track_2, track_3, track_4. so on.
You can't create dynamically-named variables. All you can do - it to create some collection or array, and operate with it.
I think the best class for you is generic List<>:
List<String> listWithDynamic = new List<String>();
for (int i = 1; i < limit; i +=1)
{
listWithDynamic.Add(string.Format("track_{0}", i));
...
}
Assuming you want strings:
for (int i = 1; i < limit; i +=1)
{
string track = string.Format("track_{0}", i);
...
}
But when you already have variables called track_1, track_2, track_3, track_4 you will need an array or List:
var tracks = new TrackType[] { track_1, track_2, track_3, track_4 } ;
for (int i = 0; i < tracks.length; i++)
{
var track = tracks[i]; // tracks[0] == track_1
...
}
Obvious Solution
for (var i = 0; i < 10; i++)
{
var track = string.Format("track_{0}", i);
}
Linq-Based Solution
foreach (var track in Enumerable.Range(0, 100).Select(x => string.Format("track_{0}", x)))
{
}
Operator-Based Solution This is somewhat hacky, but fun none-the-less.
for (var i = new Frob(0, "track_{0}"); i < 100; i++)
{
Console.WriteLine(i.ValueDescription);
}
struct Frob
{
public int Value { get; private set; }
public string ValueDescription { get; private set; }
private string _format;
public Frob(int value, string format)
: this()
{
Value = value;
ValueDescription = string.Format(format, value);
_format = format;
}
public static Frob operator ++(Frob value)
{
return new Frob(value.Value + 1, value._format);
}
public static Frob operator --(Frob value)
{
return new Frob(value.Value - 1, value._format);
}
public static implicit operator int(Frob value)
{
return value.Value;
}
public static implicit operator string(Frob value)
{
return value.ValueDescription;
}
public override bool Equals(object obj)
{
if (obj is Frob)
{
return ((Frob)obj).Value == Value;
}
else if (obj is string)
{
return ((string)obj) == ValueDescription;
}
else if (obj is int)
{
return ((int)obj) == Value;
}
else
{
return base.Equals(obj);
}
}
public override int GetHashCode()
{
return Value;
}
public override string ToString()
{
return ValueDescription;
}
}
don't know if I get your question, but I will try:
for(var i = 1; i < yourExclusiveUpperbound; i++)
{
var track = String.Format("$track_{0}", i);
// use track
}
or with some LINQ-Magic:
foreach(var track in Enumerate.Range(1, count)
.Select(i => String.Format("$track_{0}", i)))
{
// use track
}
Do as follow:
for (int i = 0; i < lenght; i ++)
{
any work do in loop
}
No, we can't create dynamically named variables in a loop. But, there are other elegant ways to address the problem instead of creating dynamically named variables.
One could be, create an array or list before the loop and store values in array / list items in the loop. You can access the array / list later anywhere in your code. If you know which variable you want to use (track_1, track_2, ...), you can simply access it from the array / list (tracks[1], tracks[2], ...).
List<String> tracks = new List<String>();
for (int i = 1; i < limit; i++)
{
Track track = new Track();
tracks.Add(track);
...
}

C# hashcode for array of ints

I have a class that internally is just an array of integers. Once constructed the array never changes. I'd like to pre-compute a good hashcode so that this class can be very efficiently used as a key in a Dictionary. The length of the array is less than about 30 items, and the integers are between -1000 and 1000 in general.
Not very clever, but sufficient for most practical purposes:
EDIT: changed due to comment of Henk Holterman, thanks for that.
int hc = array.Length;
foreach (int val in array)
{
hc = unchecked(hc * 314159 + val);
}
If you need something more sophisticated, look here.
For an array of values generally between -1000 and 1000, I would probably use something like this:
static int GetHashCode(int[] values)
{
int result = 0;
int shift = 0;
for (int i = 0; i < values.Length; i++)
{
shift = (shift + 11) % 21;
result ^= (values[i]+1024) << shift;
}
return result;
}
You may use CRC32 checksum. Here is the code:
[CLSCompliant(false)]
public class Crc32 {
uint[] table = new uint[256];
uint[] Table { get { return table; } }
public Crc32() {
MakeCrcTable();
}
void MakeCrcTable() {
for (uint n = 0; n < 256; n++) {
uint value = n;
for (int i = 0; i < 8; i++) {
if ((value & 1) != 0)
value = 0xedb88320 ^ (value >> 1);
else
value = value >> 1;
}
Table[n] = value;
}
}
public uint UpdateCrc(uint crc, byte[] buffer, int length) {
uint result = crc;
for (int n = 0; n < length; n++) {
result = Table[(result ^ buffer[n]) & 0xff] ^ (result >> 8);
}
return result;
}
public uint Calculate(Stream stream) {
long pos = stream.Position;
const int size = 0x32000;
byte[] buf = new byte[size];
int bytes = 0;
uint result = 0xffffffff;
do {
bytes = stream.Read(buf, 0, size);
result = UpdateCrc(result, buf, bytes);
}
while (bytes == size);
stream.Position = pos;
return ~result;
}
}
I think choosing a good hash-algorithm would have to be based on the distribution (in a probability sense) of the integer values.
Have a look at Wikipedia for a list of algorithms
Any CRC (or even XOR) should be ok.
You could take a different approach and use a recursive dictionary for each value in your int array. This way you can leave .net to do primitive type hashing.
internal class DictionaryEntry<TKey, TValue>
{
public Dictionary<TKey, DictionaryEntry<TKey, TValue>> Children { get; private set; }
public TValue Value { get; private set; }
public bool HasValue { get; private set; }
public void SetValue(TValue value)
{
Value = value;
HasValue = true;
}
public DictionaryEntry()
{
Children = new Dictionary<TKey, DictionaryEntry<TKey, TValue>>();
}
}
internal class KeyStackDictionary<TKey, TValue>
{
// Helper dictionary to work with a stack of keys
// Usage:
// var dict = new KeyStackDictionary<int, string>();
// int[] keyStack = new int[] {23, 43, 54};
// dict.SetValue(keyStack, "foo");
// string value;
// if (dict.GetValue(keyStack, out value))
// {
// }
private DictionaryEntry<TKey, TValue> _dict;
public KeyStackDictionary()
{
_dict = new DictionaryEntry<TKey, TValue>();
}
public void SetValue(TKey[] keyStack, TValue value)
{
DictionaryEntry<TKey, TValue> dict = _dict;
for (int i = 0; i < keyStack.Length; i++)
{
TKey key = keyStack[i];
if (dict.Children.ContainsKey(key))
{
dict = dict.Children[key];
}
else
{
var child = new DictionaryEntry<TKey, TValue>();
dict.Children.Add(key, child);
dict = child;
}
if (i == keyStack.Length - 1)
{
dict.SetValue(value);
}
}
}
// returns false if the value is not found using the key stack
public bool GetValue(TKey[] keyStack, out TValue value)
{
DictionaryEntry<TKey, TValue> dict = _dict;
for (int i = 0; i < keyStack.Length; i++)
{
TKey key = keyStack[i];
if (dict.Children.ContainsKey(key))
{
dict = dict.Children[key];
}
else
{
break;
}
if (i == keyStack.Length - 1 && dict.HasValue)
{
value = dict.Value;
return true;
}
}
value = default(TValue);
return false;
}
}
You can use Linq methods too:
var array = new int[10];
var hashCode = array.Aggregate(0, (a, v) =>
HashCode.Combine(a, v.GetHashCode()));
I'm using this here
var arrayHash = string.Join(string.Empty, array).GetHashCode();
If a element changed in the array, you will get a new hash.
I would recommend:
HashCode.Combine(array)
For .NET Core 2.1 / .NET Standard 2.1 / .NET 5 and later.

C# Extend array type to overload operators

I'd like to create my own class extending array of ints. Is that possible? What I need is array of ints that can be added by "+" operator to another array (each element added to each), and compared by "==", so it could (hopefully) be used as a key in dictionary.
The thing is I don't want to implement whole IList interface to my new class, but only add those two operators to existing array class.
I'm trying to do something like this:
class MyArray : Array<int>
But it's not working that way obviously ;).
Sorry if I'm unclear but I'm searching solution for hours now...
UPDATE:
I tried something like this:
class Zmienne : IEquatable<Zmienne>
{
public int[] x;
public Zmienne(int ilosc)
{
x = new int[ilosc];
}
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
{
return false;
}
return base.Equals((Zmienne)obj);
}
public bool Equals(Zmienne drugie)
{
if (x.Length != drugie.x.Length)
return false;
else
{
for (int i = 0; i < x.Length; i++)
{
if (x[i] != drugie.x[i])
return false;
}
}
return true;
}
public override int GetHashCode()
{
int hash = x[0].GetHashCode();
for (int i = 1; i < x.Length; i++)
hash = hash ^ x[i].GetHashCode();
return hash;
}
}
Then use it like this:
Zmienne tab1 = new Zmienne(2);
Zmienne tab2 = new Zmienne(2);
tab1.x[0] = 1;
tab1.x[1] = 1;
tab2.x[0] = 1;
tab2.x[1] = 1;
if (tab1 == tab2)
Console.WriteLine("Works!");
And no effect. I'm not good with interfaces and overriding methods unfortunately :(. As for reason I'm trying to do it. I have some equations like:
x1 + x2 = 0.45
x1 + x4 = 0.2
x2 + x4 = 0.11
There are a lot more of them, and I need to for example add first equation to second and search all others to find out if there is any that matches the combination of x'es resulting in that adding.
Maybe I'm going in totally wrong direction?
For a single type, it is pretty easy to encapsulate, as below. Note that as a key you want to make it immutable too. If you want to use generics, it gets harder (ask for more info):
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
static class Program {
static void Main() {
MyVector x = new MyVector(1, 2, 3), y = new MyVector(1, 2, 3),
z = new MyVector(4,5,6);
Console.WriteLine(x == y); // true
Console.WriteLine(x == z); // false
Console.WriteLine(object.Equals(x, y)); // true
Console.WriteLine(object.Equals(x, z)); // false
var comparer = EqualityComparer<MyVector>.Default;
Console.WriteLine(comparer.GetHashCode(x)); // should match y
Console.WriteLine(comparer.GetHashCode(y)); // should match x
Console.WriteLine(comparer.GetHashCode(z)); // *probably* different
Console.WriteLine(comparer.Equals(x,y)); // true
Console.WriteLine(comparer.Equals(x,z)); // false
MyVector sum = x + z;
Console.WriteLine(sum);
}
}
public sealed class MyVector : IEquatable<MyVector>, IEnumerable<int> {
private readonly int[] data;
public int this[int index] {
get { return data[index]; }
}
public MyVector(params int[] data) {
if (data == null) throw new ArgumentNullException("data");
this.data = (int[])data.Clone();
}
private int? hash;
public override int GetHashCode() {
if (hash == null) {
int result = 13;
for (int i = 0; i < data.Length; i++) {
result = (result * 7) + data[i];
}
hash = result;
}
return hash.GetValueOrDefault();
}
public int Length { get { return data.Length; } }
public IEnumerator<int> GetEnumerator() {
for (int i = 0; i < data.Length; i++) {
yield return data[i];
}
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
public override bool Equals(object obj)
{
return this == (obj as MyVector);
}
public bool Equals(MyVector obj) {
return this == obj;
}
public override string ToString() {
StringBuilder sb = new StringBuilder("[");
if (data.Length > 0) sb.Append(data[0]);
for (int i = 1; i < data.Length; i++) {
sb.Append(',').Append(data[i]);
}
sb.Append(']');
return sb.ToString();
}
public static bool operator ==(MyVector x, MyVector y) {
if(ReferenceEquals(x,y)) return true;
if(ReferenceEquals(x,null) || ReferenceEquals(y,null)) return false;
if (x.hash.HasValue && y.hash.HasValue && // exploit known different hash
x.hash.GetValueOrDefault() != y.hash.GetValueOrDefault()) return false;
int[] xdata = x.data, ydata = y.data;
if(xdata.Length != ydata.Length) return false;
for(int i = 0 ; i < xdata.Length ; i++) {
if(xdata[i] != ydata[i]) return false;
}
return true;
}
public static bool operator != (MyVector x, MyVector y) {
return !(x==y);
}
public static MyVector operator +(MyVector x, MyVector y) {
if(x==null || y == null) throw new ArgumentNullException();
int[] xdata = x.data, ydata = y.data;
if(xdata.Length != ydata.Length) throw new InvalidOperationException("Length mismatch");
int[] result = new int[xdata.Length];
for(int i = 0 ; i < xdata.Length ; i++) {
result[i] = xdata[i] + ydata[i];
}
return new MyVector(result);
}
}
Its not permitted to extend the array class, see the reference: http://msdn.microsoft.com/en-us/library/system.array.aspx
You could either implement IList (which has the basic methods), or encapsulate an Array in your class and provide conversion operators.
Please let me know if you need more detail.
Can you not just use the List class? This already does what you want via the AddRange method.
implement the ienumerable interface

Categories

Resources