Hashtable 类

表示键/值对的集合,这些键/值对根据键的哈希代码进行组织。

命名空间:System.Collections

备注

每个元素都是一个存储在 DictionaryEntry 对象中的键/值对。键不能为 空引用(在 Visual Basic 中为 Nothing),但值可以。

要重写 Object.GetHashCode 方法(或 IHashCodeProvider 接口)和 Object.Equals 方法(或 IComparer 接口),需要有被 Hashtable 用作键的对象。方法和接口的实现必须以相同的方式处理大小写;否则,Hashtable 的行为可能不正确。例如,创建 Hashtable 时,您必须配合使用 CaseInsensitiveHashCodeProvider 类(或任何不区分大小写的 IHashCodeProvider 实现)和 CaseInsensitiveComparer 类(或任何不区分大小写的 IComparer 实现)。

此外,如果该键存在于 Hashtable 中,那么当使用相同参数调用这些方法时,这些方法必须生成相同的结果。还有一种方式是使用具有 IEqualityComparer 参数的 Hashtable 构造函数。如果键相等性只是引用相等性,则 Object.GetHashCodeObject.Equals 的继承实现将满足需要。

只要键对象用作 Hashtable 中的键,它们就必须是永远不变的。

当把某个元素添加到 Hashtable 时,将根据键的哈希代码将该元素放入存储桶中。该键的后续查找将使用键的哈希代码只在一个特定存储桶中搜索,这将大大减少为查找一个元素所需的键比较的次数。

Hashtable 的加载因子确定元素与存储桶的最大比率。加载因子越小,平均查找速度越快,但消耗的内存也增加。默认的加载因子 1.0 通常提供速度和大小之间的最佳平衡。当创建 Hashtable 时,也可以指定其他加载因子。

当向 Hashtable 添加元素时,Hashtable 的实际加载因子将增加。当实际加载因子达到指定的加载因子时,Hashtable 中存储桶的数目自动增加到大于当前 Hashtable 存储桶数两倍的最小质数。

Hashtable 中的每个键对象必须提供其自己的哈希函数,可通过调用 GetHash 访问该函数。但是,可将任何实现 IHashCodeProvider 的对象传递到 Hashtable 构造函数,而且该哈希函数用于该表中的所有对象。

Hashtable 的容量是 Hashtable 可拥有的元素数。Hashtable 的默认初始容量为零。随着向 Hashtable 中添加元素,容量通过重新分配按需自动增加。

C# 语言中的 foreach 语句(在 Visual Basic 中为 for each)需要集合中每个元素的类型。由于 Hashtable 的每个元素都是一个键/值对,因此元素类型既不是键的类型,也不是值的类型。而是 DictionaryEntry 类型。例如:

foreach (DictionaryEntry de in myHashtable) {...}

foreach 语句是对枚举数的包装,它只允许从集合中读取,不允许写入集合。

因为序列化和反序列化 Hashtable 的枚举数会使元素重新排序,所以不调用 Reset 方法就不能继续枚举。

示例

下面的示例说明如何对 Hashtable 创建、初始化并执行各种函数以及如何打印出其键和值。

using System;
using System.Collections;

class Example
{
    public static void Main()
    {
        //创建一个新的哈希表
        //
        Hashtable openWith = new Hashtable();

        //将一些元素添加到散列表中。 没有重复的键,但其中一些值是重复的。
        openWith.Add("txt", "notepad.exe");
        openWith.Add("bmp", "paint.exe");
        openWith.Add("dib", "paint.exe");
        openWith.Add("rtf", "wordpad.exe");

        // 如果新密钥已存在于散列表中,则Add方法将引发异常。
        try
        {
            openWith.Add("txt", "winword.exe");
        }
        catch
        {
            Console.WriteLine("An element with Key = \"txt\" already exists.");
        }

        //Item属性是默认属性,所以在访问元素时可以省略它的名字。
        Console.WriteLine("For key = \"rtf\", value = {0}.", openWith["rtf"]);

        //默认的Item属性可以用来改变与某个键相关的值。
        openWith["rtf"] = "winword.exe";
        Console.WriteLine("For key = \"rtf\", value = {0}.", openWith["rtf"]);

        //如果某个键不存在,则为该键设置默认的Item属性会添加一个新的键/值对。
        openWith["doc"] = "winword.exe";

        //如果请求的key不在散列表中,默认的Item属性会引发异常。
        try
        {
            Console.WriteLine("For key = \"tif\", value = {0}.", openWith["tif"]);
        }
        catch
        {
            Console.WriteLine("Key = \"tif\" is not found.");
        }

        // 在插入它们之前,可以使用ContainsKey来测试Key。
        if (!openWith.ContainsKey("ht"))
        {
            openWith.Add("ht", "hypertrm.exe");
            Console.WriteLine("Value added for key = \"ht\": {0}", openWith["ht"]);
        }

        //当您使用foreach枚举散列表元素时,元素将被检索为KeyValuePair对象。
        Console.WriteLine();
        foreach( DictionaryEntry de in openWith )
        {
            Console.WriteLine("Key = {0}, Value = {1}", de.Key, de.Value);
        }

        //要单独获取值,请使用Values属性。
        ICollection valueColl = openWith.Values;

        //ValueCollection的元素使用为散列表值指定的类型进行强类型化。
        Console.WriteLine();
        foreach( string s in valueColl )
        {
            Console.WriteLine("Value = {0}", s);
        }

        //要单独获取keys,请使用Keys属性。
        ICollection keyColl = openWith.Keys;

        // KeyCollection的元素使用为散列表键指定的类型强类型化。
        Console.WriteLine();
        foreach( string s in keyColl )
        {
            Console.WriteLine("Key = {0}", s);
        }

        //使用Remove方法删除一个键/值对。
        Console.WriteLine("\nRemove(\"doc\")");
        openWith.Remove("doc");

        if (!openWith.ContainsKey("doc"))
        {
            Console.WriteLine("Key \"doc\" is not found.");
        }
    }
}

此代码示例生成以下输出:

/* 
An element with Key = "txt" already exists.
For key = "rtf", value = wordpad.exe.
For key = "rtf", value = winword.exe.
For key = "tif", value = .
Value added for key = "ht": hypertrm.exe

Key = dib, Value = paint.exe
Key = txt, Value = notepad.exe
Key = ht, Value = hypertrm.exe
Key = bmp, Value = paint.exe
Key = rtf, Value = winword.exe
Key = doc, Value = winword.exe

Value = paint.exe
Value = notepad.exe
Value = hypertrm.exe
Value = paint.exe
Value = winword.exe
Value = winword.exe

Key = dib
Key = txt
Key = ht
Key = bmp
Key = rtf
Key = doc

Remove("doc")
Key "doc" is not found.
 */

线程安全

Hashtable 是线程安全的,可由多个读取器线程或一个写入线程使用。多线程使用时,如果任何一个线程执行写入(更新)操作,它都不是线程安全的。若要支持多个编写器,如果没有任何线程在读取 Hashtable 对象,则对 Hashtable 的所有操作都必须通过 Synchronized 方法返回的包装完成。

从头到尾对一个集合进行枚举本质上并不是一个线程安全的过程。即使一个集合已进行同步,其他线程仍可以修改该集合,这将导致枚举数引发异常。若要在枚举过程中保证线程安全,可以在整个枚举过程中锁定集合,或者捕捉由于其他线程进行的更改而引发的异常。


Hashtable.Synchronized 方法

返回 Hashtable 的同步(线程安全)包装。

备注

如果没有任何线程在读取 Hashtable,则 Synchronized 支持使用多个写入线程。如果使用一个或多个读取器以及一个或多个编写器,则同步包装不提供线程安全的访问。

从头到尾对一个集合进行枚举本质上并不是一个线程安全的过程。即使一个集合已进行同步,其他线程仍可以修改该集合,这将导致枚举数引发异常。若要在枚举过程中保证线程安全,可以在整个枚举过程中锁定集合,或者捕捉由于其他线程进行的更改而引发的异常。

下面的代码示例显示如何在整个枚举过程中使用 SyncRoot 锁定集合:

Hashtable myCollection = new Hashtable();
  lock(myCollection.SyncRoot) {
  foreach (Object item in myCollection) {
  // 在这里插入你的代码
  }
 }

此方法是 O(1) 操作。

示例

下面的示例演示如何同步 Hashtable、如何确定 Hashtable 是否同步以及如何使用同步的 Hashtable

using System;
using System.Collections;
public class SamplesHashtable  {

   public static void Main()  {

      // 创建并初始化一个新的Hashtable。
      Hashtable myHT = new Hashtable();
      myHT.Add( 0, "zero" );
      myHT.Add( 1, "one" );
      myHT.Add( 2, "two" );
      myHT.Add( 3, "three" );
      myHT.Add( 4, "four" );

      //在Hashtable周围创建一个同步包装。
      Hashtable mySyncdHT = Hashtable.Synchronized( myHT );

      // 显示两个哈希表的同步状态。
      Console.WriteLine( "myHT is {0}.", myHT.IsSynchronized ? "synchronized" : "not synchronized" );
      Console.WriteLine( "mySyncdHT is {0}.", mySyncdHT.IsSynchronized ? "synchronized" : "not synchronized" );
   }
}

该代码产生以下输出
/* 


myHT is not synchronized.
mySyncdHT is synchronized.
*/ 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值