⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 multidictionary.cs

📁 C#写的类似于STL的集合类,首先是C#编写,可以用于.net变程.
💻 CS
📖 第 1 页 / 共 2 页
字号:
//******************************
// Written by Peter Golde
// Copyright (c) 2004-2005, Wintellect
//
// Use and restribution of this code is subject to the license agreement 
// contained in the file "License.txt" accompanying this file.
//******************************

using System;
using System.Collections;
using System.Collections.Generic;

namespace Wintellect.PowerCollections
{
    /// <summary>
    /// <para>The MultiDictionary class that associates values with a key. Unlike an Dictionary,
    /// each key can have multiple values associated with it. When indexing an MultiDictionary, instead
    /// of a single value associated with a key, you retrieve an enumeration of values.</para>
    /// <para>When constructed, you can chose to allow the same value to be associated with a key multiple
    /// times, or only one time. </para>
    /// </summary>
    /// <typeparam name="TKey">The type of the keys.</typeparam>
    /// <typeparam name="TValue">The of values associated with the keys.</typeparam>
    ///<seealso cref="Dictionary&lt;TKey,TValue&gt;"/>
    ///<seealso cref="OrderedMultiDictionary&lt;TKey,TValue&gt;"/>
    [Serializable]
    public class MultiDictionary<TKey, TValue> : MultiDictionaryBase<TKey, TValue>, ICloneable
    {
        // The comparer for comparing keys
        private IEqualityComparer<TKey> keyEqualityComparer;

        // The comparer for comparing values;
        private IEqualityComparer<TValue> valueEqualityComparer;

        // The comparer for compaing keys and values.
        private IEqualityComparer<KeyAndValues> equalityComparer;

        // The hash that holds the keys and values.
        private Hash<KeyAndValues> hash;

        // Whether duplicate values for the same key are allowed.
        private bool allowDuplicateValues;

        /// <summary>
        /// A structure to hold the key and the values associated with the key.
        /// The number of values must always be 1 or greater in a version that is stored, but 
        /// can be zero in a dummy version used only for lookups.
        /// </summary>
        [Serializable]
        private struct KeyAndValues
        {
            /// <summary>
            /// The key.
            /// </summary>
            public TKey Key;

            /// <summary>
            /// The number of values. Always at least 1 except in a dummy version for lookups.
            /// </summary>
            public int Count;

            /// <summary>
            /// An array of values. 
            /// </summary>
            public TValue[] Values;

            /// <summary>
            /// Create a dummy KeyAndValues with just the key, for lookups.
            /// </summary>
            /// <param name="key">The key to use.</param>
            public KeyAndValues(TKey key)
            {
                this.Key = key;
                this.Count = 0;
                this.Values = null;
            }

            /// <summary>
            /// Make a copy of a KeyAndValues, copying the array.
            /// </summary>
            /// <param name="x">KeyAndValues to copy.</param>
            /// <returns>A copied version.</returns>
            public static KeyAndValues Copy(KeyAndValues x)
            {
                KeyAndValues result;

                result.Key = x.Key;
                result.Count = x.Count;

                if (x.Values != null)
                    result.Values = (TValue[])x.Values.Clone();
                else
                    result.Values = null;

                return result;
            }
        }

        /// <summary>
        /// This class implements IEqualityComparer for KeysAndValues, allowing them to be
        /// compared by their keys. An IEqualityComparer on keys is required.
        /// </summary>
        [Serializable]
        private class KeyAndValuesEqualityComparer : IEqualityComparer<KeyAndValues>
        {
            private IEqualityComparer<TKey> keyEqualityComparer;

            public KeyAndValuesEqualityComparer(IEqualityComparer<TKey> keyEqualityComparer)
            {
                this.keyEqualityComparer = keyEqualityComparer;
            }

            public bool Equals(KeyAndValues x, KeyAndValues y)
            {
                return keyEqualityComparer.Equals(x.Key, y.Key);
            }

            public int GetHashCode(KeyAndValues obj)
            {
                return Util.GetHashCode(obj.Key, keyEqualityComparer);
            }
        }


        #region Constructors

        /// <summary>
        /// Create a new MultiDictionary. The default ordering of keys and values are used. If duplicate values
        /// are allowed, multiple copies of the same value can be associated with the same key. For example, the key "foo"
        /// could have "a", "a", and "b" associated with it. If duplicate values are not allowed, only one copies of a given value can
        /// be associated with the same key, although different keys can have the same value. For example, the key "foo" could
        /// have "a" and "b" associated with it, which key "bar" has values "b" and "c" associated with it.
        /// </summary>
        /// <remarks>The default ordering of keys and values will be used, as defined by TKey and TValue's implementation
        /// of IComparable&lt;T&gt; (or IComparable if IComparable&lt;T&gt; is not implemented). If a different ordering should be
        /// used, other constructors allow a custom Comparer or IComparer to be passed to changed the ordering.</remarks>
        /// <param name="allowDuplicateValues">Can the same value be associated with a key multiple times?</param>
        /// <exception cref="InvalidOperationException">TKey or TValue does not implement either IComparable&lt;T&gt; or IComparable.</exception>
        public MultiDictionary(bool allowDuplicateValues)
            : this(allowDuplicateValues, EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
        {
        }

        /// <summary>
        /// Create a new MultiDictionary. If duplicate values
        /// are allowed, multiple copies of the same value can be associated with the same key. For example, the key "foo"
        /// could have "a", "a", and "b" associated with it. If duplicate values are not allowed, only one copies of a given value can
        /// be associated with the same key, although different keys can have the same value. For example, the key "foo" could
        /// have "a" and "b" associated with it, which key "bar" has values "b" and "c" associated with it.
        /// </summary>
        /// <param name="allowDuplicateValues">Can the same value be associated with a key multiple times?</param>
        /// <param name="keyEqualityComparer">An IEqualityComparer&lt;TKey&gt; instance that will be used to compare keys.</param>
        /// <exception cref="InvalidOperationException">TValue does not implement either IComparable&lt;TValue&gt; or IComparable.</exception>
        public MultiDictionary(bool allowDuplicateValues, IEqualityComparer<TKey> keyEqualityComparer)
            : this(allowDuplicateValues, keyEqualityComparer, EqualityComparer<TValue>.Default)
        {
        }

        /// <summary>
        /// Create a new MultiDictionary. If duplicate values
        /// are allowed, multiple copies of the same value can be associated with the same key. For example, the key "foo"
        /// could have "a", "a", and "b" associated with it. If duplicate values are not allowed, only one copies of a given value can
        /// be associated with the same key, although different keys can have the same value. For example, the key "foo" could
        /// have "a" and "b" associated with it, which key "bar" has values "b" and "c" associated with it.
        /// </summary>
        /// <param name="allowDuplicateValues">Can the same value be associated with a key multiple times?</param>
        /// <param name="keyEqualityComparer">An IEqualityComparer&lt;TKey&gt; instance that will be used to compare keys.</param>
        /// <param name="valueEqualityComparer">An IEqualityComparer&lt;TValue&gt; instance that will be used to compare values.</param>
        public MultiDictionary(bool allowDuplicateValues, IEqualityComparer<TKey> keyEqualityComparer, IEqualityComparer<TValue> valueEqualityComparer)
        {
            if (keyEqualityComparer == null)
                throw new ArgumentNullException("keyEqualityComparer");
            if (valueEqualityComparer == null)
                throw new ArgumentNullException("valueEqualityComparer");

            this.allowDuplicateValues = allowDuplicateValues;
            this.keyEqualityComparer = keyEqualityComparer;
            this.valueEqualityComparer = valueEqualityComparer;
            this.equalityComparer = new KeyAndValuesEqualityComparer(keyEqualityComparer);
            this.hash = new Hash<KeyAndValues>(equalityComparer);
        }

        /// <summary>
        /// Create a new MultiDictionary. Private constructor, for use by Clone().
        /// </summary>
        private MultiDictionary(bool allowDuplicateValues, IEqualityComparer<TKey> keyEqualityComparer, IEqualityComparer<TValue> valueEqualityComparer, IEqualityComparer<KeyAndValues> equalityComparer, Hash<KeyAndValues> hash)
        {
            if (keyEqualityComparer == null)
                throw new ArgumentNullException("keyEqualityComparer");
            if (valueEqualityComparer == null)
                throw new ArgumentNullException("valueEqualityComparer");

            this.allowDuplicateValues = allowDuplicateValues;
            this.keyEqualityComparer = keyEqualityComparer;
            this.valueEqualityComparer = valueEqualityComparer;
            this.equalityComparer = equalityComparer;
            this.hash = hash;
        }


        #endregion Constructors

        #region Add or remove items

        /// <summary>
        /// <para>Adds a new value to be associated with a key. If duplicate values are permitted, this
        /// method always adds a new key-value pair to the dictionary.</para>
        /// <para>If duplicate values are not permitted, and <paramref name="key"/> already has a value
        /// equal to <paramref name="value"/> associated with it, then that value is replaced with <paramref name="value"/>,
        /// and the number of values associate with <paramref name="key"/> is unchanged.</para>
        /// </summary>
        /// <param name="key">The key to associate with.</param>
        /// <param name="value">The value to associated with <paramref name="key"/>.</param>
        public sealed override void Add(TKey key, TValue value)
        {
            KeyAndValues keyValues = new KeyAndValues(key);
            KeyAndValues existing;

            if (hash.Find(keyValues, false, out existing)) {
                // There already is an item in the hash table equal to this key. Add the new value,
                // taking into account duplicates if needed.
                int existingCount = existing.Count;
                if (!allowDuplicateValues) {
                    int valueHash = Util.GetHashCode(value, valueEqualityComparer);
                    for (int i = 0; i < existingCount; ++i) {
                        if (Util.GetHashCode(existing.Values[i], valueEqualityComparer) == valueHash &&
                            valueEqualityComparer.Equals(existing.Values[i], value)) {
                            // Found an equal existing value. Replace it and we're done.
                            existing.Values[i] = value;
                            return;
                        }
                    }
                }

                // Add a new value to an existing key.
                if (existingCount == existing.Values.Length) {
                    // Grow the array to make room.
                    TValue[] newValues = new TValue[existingCount * 2];
                    Array.Copy(existing.Values, newValues, existingCount);
                    existing.Values = newValues;
                }
                existing.Values[existingCount] = value;
                existing.Count = existingCount + 1;

                // Update the hash table.
                hash.Find(existing, true, out keyValues);
                return;
            }
            else {
                // No item with this key. Add it.
                keyValues.Count = 1;
                keyValues.Values = new TValue[1] { value };
                hash.Insert(keyValues, true, out existing);
                return;
            }
        }

        /// <summary>
        /// Removes a given value from the values associated with a key. If the
        /// last value is removed from a key, the key is removed also.
        /// </summary>
        /// <param name="key">A key to remove a value from.</param>
        /// <param name="value">The value to remove.</param>
        /// <returns>True if <paramref name="value"/> was associated with <paramref name="key"/> (and was
        /// therefore removed). False if <paramref name="value"/> was not associated with <paramref name="key"/>.</returns>
        public sealed override bool Remove(TKey key, TValue value)
        {
            KeyAndValues keyValues = new KeyAndValues(key);
            KeyAndValues existing;

            if (hash.Find(keyValues, false, out existing)) {
                // There is an item in the hash table equal to this key. Find the value.
                int existingCount = existing.Count;
                int valueHash = Util.GetHashCode(value, valueEqualityComparer);
                int indexFound = -1;
                for (int i = 0; i < existingCount; ++i) {
                    if (Util.GetHashCode(existing.Values[i], valueEqualityComparer) == valueHash &&
                        valueEqualityComparer.Equals(existing.Values[i], value)) 
                    {
                        // Found an equal existing value
                        indexFound = i;
                    }
                }

                if (existingCount == 1) {
                    // Removing the last value. Remove the key.
                    hash.Delete(existing, out keyValues);
                    return true;
                }
                else if (indexFound >= 0) {
                    // Found a value. Remove it.
                    if (indexFound < existingCount - 1)
                        Array.Copy(existing.Values, indexFound + 1, existing.Values, indexFound, existingCount - indexFound - 1);
                    existing.Count = existingCount - 1;

                    // Update the hash.
                    hash.Find(existing, true, out keyValues);
                    return true;
                }
                else {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -