C# Hashtable 的通用版本是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/772831/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 00:33:56  来源:igfitidea点击:

What is the generic version of a Hashtable?

c#.netgenerics

提问by Charlie

I have been learning the basics of generics in .NET. However, I don't see the generic equivalent of Hashtable. Please share some sample C# code for creating generic hashtable classes.

我一直在学习 .NET 中泛型的基础知识。但是,我没有看到Hashtable. 请分享一些用于创建通用哈希表类的示例 C# 代码。

采纳答案by Joel Coehoorn

Dictionary<TKey, TValue>

Dictionary<TKey, TValue>

Note that Dictionary is not a 100% drop in replacement for HashTable.

请注意,Dictionary 并不是 HashTable 的 100% 替代品。

There is a slight difference in the way they handle NULLs. The dictionary will throw an exception if you try to reference a key that doesn't exist. The HashTable will just return null. The reason is that the value might be a value type, which cannotbe null. In a Hashtable the value was always Object, so returning null was at least possible.

它们处理 NULL 的方式略有不同。如果您尝试引用不存在的键,字典将抛出异常。HashTable 只会返回 null。原因是该值可能是值类型,不能为空。在 Hashtable 中,值始终是 Object,因此至少可以返回 null。

回答by Gulzar Nazim

The generic version of Hashtable class is System.Collections.Generic.Dictionaryclass.

Hashtable 类的通用版本是System.Collections.Generic.Dictionary类。

Sample code:

示例代码

Dictionary<int, string> numbers = new Dictionary<int, string>( );
   numbers.Add(1, "one");
   numbers.Add(2, "two");
   // Display all key/value pairs in the Dictionary.
   foreach (KeyValuePair<int, string> kvp in numbers)
   {
      Console.WriteLine("Key: " + kvp.Key + "\tValue: " + kvp.Value);
   }

回答by JaredPar

The generic version of a Hashtable is the Dictionary<TKey,TValue>class (link). Here is some sample code translated from using a Hashtable into the most direct equivalent of Dictionary (argument checking removed for sake of brevity)

哈希表的通用版本是Dictionary<TKey,TValue>类(链接)。下面是一些示例代码,从使用 Hashtable 转换为最直接的 Dictionary 等价物(为简洁起见,删除了参数检查)

public HashTable Create(int[] keys, string[] values) { 
  HashTable table = new HashTable();
  for ( int i = 0; i < keys.Length; i++ ) {
    table[keys[i]] = values[i];
  }
  return table;
}

public Dictionary<object,object> Create(int[] keys, string[] values) {
  Dictionary<object,object> map = Dictionary<object,object>();
  for ( int i = 0; i < keys.Length; i++) {
    map[keys[i]] = values[i];
  }
  return map;
}

That's a fairly direct translation. But the problem is that this does not actually take advantage of the type safe features of generics. The second function could be written as follows and be much more type safe and inccur no boxing overhead

这是一个相当直接的翻译。但问题是这实际上并没有利用泛型的类型安全特性。第二个函数可以写成如下,并且更加类型安全并且不会产生装箱开销

public Dictionary<int,string> Create(int[] keys, string[] values) {
  Dictionary<int,string> map = Dictionary<int,string>();
  for ( int i = 0; i < keys.Length; i++) {
    map[keys[i]] = values[i];
  }
  return map;
}

Even better. Here's a completely generic version

甚至更好。这是一个完全通用的版本

public Dictionary<TKey,TValue> Create<TKey,TValue>(TKey[] keys, TValue[] values) {
  Dictionary<TKey,TValue> map = Dictionary<TKey,TValue>();
  for ( int i = 0; i < keys.Length; i++) {
    map[keys[i]] = values[i];
  }
  return map;
}

And one that is even further flexible (thanks Joel for pointing out I missed this)

还有一个更灵活的(感谢乔尔指出我错过了这一点)

public Dictionary<TKey,TValue> Create<TKey,TValue>(
    IEnumerable<TKey> keys, 
    IEnumerable<TValue> values) {

  Dictionary<TKey,TValue> map = Dictionary<TKey,TValue>();
  using ( IEnumerater<TKey> keyEnum = keys.GetEnumerator() ) 
  using ( IEnumerator<TValue> valueEnum = values.GetEnumerator()) {
    while (keyEnum.MoveNext() && valueEnum.MoveNext() ) { 
      map[keyEnum.Current] = valueEnum.Current;
    }
  }
  return map;
}

回答by Michael Erickson

For those who are interested, I created a generic Hashtable wrapper class, which is useful for enforcing type safety and can be passed as a generic IDictionary, ICollection and IEnumerable type, whereas the non-generic Hashtable cannot. Below is the implementation.

对于那些感兴趣的人,我创建了一个通用的 Hashtable 包装类,它对于强制类型安全很有用,并且可以作为通用的 IDictionary、ICollection 和 IEnumerable 类型传递,而非通用的 Hashtable 不能。下面是实现。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Common.Collections.Generic
{
    public class Hashtable<TKey, TValue> : IDictionary<TKey, TValue>
        , ICollection<KeyValuePair<TKey, TValue>>
        , IEnumerable<KeyValuePair<TKey, TValue>>
        , IDictionary
        , ICollection
        , IEnumerable
    {
        protected Hashtable _items;
        /// <summary>
        /// Initializes a new, empty instance of the Hashtable class using the default initial capacity, load factor, hash code provider, and comparer.
        /// </summary>
        public Hashtable()
        {
            _items = new Hashtable();
        }
        /// <summary>
        /// Initializes a new, empty instance of the Hashtable class using the specified initial capacity, and the default load factor, hash code provider, and comparer.
        /// </summary>
        /// <param name="capacity">The approximate number of elements that the Hashtable object can initially contain. </param>
        public Hashtable(int capacity)
        {
            _items = new Hashtable(capacity);
        }
        /// <summary>
        /// Actual underlying hashtable object that contains the elements.
        /// </summary>
        public Hashtable Items { get { return _items; } }

        /// <summary>
        /// Adds an element with the specified key and value into the Hashtable.
        /// </summary>
        /// <param name="key">Key of the new element to add.</param>
        /// <param name="value">Value of the new elment to add.</param>
        public void Add(TKey key, TValue value)
        {
            _items.Add(key, value);
        }
        /// <summary>
        /// Adds an element with the specified key and value into the Hashtable.
        /// </summary>
        /// <param name="item">Item containing the key and value to add.</param>
        public void Add(KeyValuePair<TKey, TValue> item)
        {
            _items.Add(item.Key, item.Value);
        }

        void IDictionary.Add(object key, object value)
        {
            this.Add((TKey)key, (TValue)value);
        }
        /// <summary>
        /// Add a list of key/value pairs to the hashtable.
        /// </summary>
        /// <param name="collection">List of key/value pairs to add to hashtable.</param>
        public void AddRange(IEnumerable<KeyValuePair<TKey, TValue>> collection)
        {
            foreach (var item in collection)
                _items.Add(item.Key, item.Value);
        }
        /// <summary>
        /// Determines whether the Hashtable contains a specific key.
        /// </summary>
        /// <param name="key">Key to locate.</param>
        /// <returns>True if key is found, otherwise false.</returns>
        public bool ContainsKey(TKey key)
        {
            return _items.ContainsKey(key);
        }
        /// <summary>
        /// Determines whether the Hashtable contains a specific key.
        /// </summary>
        /// <param name="item">Item containing the key to locate.</param>
        /// <returns>True if item.Key is found, otherwise false.</returns>
        public bool Contains(KeyValuePair<TKey, TValue> item)
        {
            return _items.ContainsKey(item.Key);
        }

        bool IDictionary.Contains(object key)
        {
            return this.ContainsKey((TKey)key);
        }
        /// <summary>
        /// Gets an ICollection containing the keys in the Hashtable.
        /// </summary>
        public ICollection<TKey> Keys
        {
            get { return _items.ToList<TKey>(); }
        }

        ICollection IDictionary.Keys
        {
            get { return this.Keys.ToList(); }
        }
        /// <summary>
        /// Gets the value associated with the specified key.
        /// </summary>
        /// <param name="key">The key of the value to get.</param>
        /// <param name="value">When this method returns, contains the value associated with the specified key,
        /// if the key is found; otherwise, the default value for the type of the value parameter. This parameter 
        /// is passed uninitialized.</param>
        /// <returns>true if the hashtable contains an element with the specified key, otherwise false.</returns>
        public bool TryGetValue(TKey key, out TValue value)
        {
            value = (TValue)_items[key];
            return (value != null);
        }
        /// <summary>
        /// Gets an ICollection containing the values in the Hashtable.
        /// </summary>
        public ICollection<TValue> Values
        {
            get { return _items.Values.ToList<TValue>(); }
        }

        ICollection IDictionary.Values
        {
            get { return this.Values.ToList(); }
        }
        /// <summary>
        /// Gets or sets the value associated with the specified key.
        /// </summary>
        /// <param name="key">The key whose value to get or set. </param>
        /// <returns>The value associated with the specified key. If the specified key is not found, 
        /// attempting to get it returns null, and attempting to set it creates a new element using the specified key.</returns>
        public TValue this[TKey key]
        {
            get
            {
                return (TValue)_items[key];
            }
            set
            {
                _items[key] = value;
            }
        }
        /// <summary>
        /// Removes all elements from the Hashtable.
        /// </summary>
        public void Clear()
        {
            _items.Clear();
        }
        /// <summary>
        /// Copies all key/value pairs in the hashtable to the specified array.
        /// </summary>
        /// <param name="array">Object array to store objects of type "KeyValuePair&lt;TKey, TValue&gt;"</param>
        /// <param name="arrayIndex">Starting index to store objects into array.</param>
        public void CopyTo(Array array, int arrayIndex)
        {
            _items.CopyTo(array, arrayIndex);
        }
        /// <summary>
        /// Copies all key/value pairs in the hashtable to the specified array.
        /// </summary>
        /// <param name="array">Object array to store objects of type "KeyValuePair&lt;TKey, TValue&gt;"</param>
        /// <param name="arrayIndex">Starting index to store objects into array.</param>
        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            _items.CopyTo(array, arrayIndex);
        }
        /// <summary>
        /// Gets the number of key/value pairs contained in the Hashtable.
        /// </summary>
        public int Count
        {
            get { return _items.Count; }
        }
        /// <summary>
        /// Gets a value indicating whether the Hashtable has a fixed size.
        /// </summary>
        public bool IsFixedSize
        {
            get { return _items.IsFixedSize; }
        }
        /// <summary>
        /// Gets a value indicating whether the Hashtable is read-only.
        /// </summary>
        public bool IsReadOnly
        {
            get { return _items.IsReadOnly; }
        }
        /// <summary>
        /// Gets a value indicating whether access to the Hashtable is synchronized (thread safe).
        /// </summary>
        public bool IsSynchronized
        {
            get { return _items.IsSynchronized; }
        }
        /// <summary>
        /// Gets an object that can be used to synchronize access to the Hashtable.
        /// </summary>
        public object SyncRoot
        {
            get { return _items.SyncRoot; }
        }
        /// <summary>
        /// Removes the element with the specified key from the Hashtable.
        /// </summary>
        /// <param name="key">Key of the element to remove.</param>
        public void Remove(TKey key)
        {
            _items.Remove(key);
        }
        /// <summary>
        /// Removes the element with the specified key from the Hashtable.
        /// </summary>
        /// <param name="item">Item containing the key of the element to remove.</param>
        public void Remove(KeyValuePair<TKey, TValue> item)
        {
            this.Remove(item.Key);
        }

        bool IDictionary<TKey, TValue>.Remove(TKey key)
        {
            var numValues = _items.Count;
            _items.Remove(key);
            return numValues > _items.Count;
        }

        bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
        {
            var numValues = _items.Count;
            _items.Remove(item.Key);
            return numValues > _items.Count;
        }

        void IDictionary.Remove(object key)
        {
            _items.Remove(key);
        }
        /// <summary>
        /// Returns an enumerator that iterates through the hashtable.
        /// </summary>
        /// <returns>An enumerator for a list of key/value pairs.</returns>
        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            foreach (DictionaryEntry? item in _items)
                yield return new KeyValuePair<TKey, TValue>((TKey)item.Value.Key, (TValue)item.Value.Value);
        }
        /// <summary>
        /// Returns an enumerator that iterates through the hashtable.
        /// </summary>
        /// <returns>An enumerator for a list of key/value pairs as generic objects.</returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        IDictionaryEnumerator IDictionary.GetEnumerator()
        {
            // Very old enumerator that no one uses anymore, not supported.
            throw new NotImplementedException(); 
        }

        object IDictionary.this[object key]
        {
            get
            {
                return _items[(TKey)key];
            }
            set
            {
                _items[(TKey)key] = value;
            }
        }
    }
}

I have done some testing of this Hashtable vs Dictionary and found the two perform about the same when used with a string key and string value pair, except the Hashtable seems to use less memory. The results of my test are as follows:

我对此 Hashtable 与 Dictionary 进行了一些测试,发现当与字符串键和字符串值对一起使用时,两者的性能大致相同,除了 Hashtable 似乎使用较少的内存。我的测试结果如下:

TestInitialize Dictionary_50K_Hashtable
Number objects 50000, memory usage 905164
Insert, 22 milliseconds.
A search not found, 0 milliseconds.
Search found, 0 milliseconds.
Remove, 0 milliseconds.
Search found or not found, 0 milliseconds.
TestCleanup Dictionary_50K_Hashtable

TestInitialize Dictionary_50K_Dictionary
Number objects 50000, memory usage 1508316
Insert, 16 milliseconds.
A search not found, 0 milliseconds.
Search found, 0 milliseconds.
Remove, 0 milliseconds.
Search found or not found, 0 milliseconds.
TestCleanup Dictionary_50K_Dictionary