8.4.2  Hashtable的代码实现

 
  哈希表的实现较为复杂,为了简化代码,本例忽略了部分出错判断,在测试时请不要设key值为空。
 
1   using System;
2   public class Hashtable
3   {
4       private struct bucket
5       {
6           public Object key; //键
7           public Object val; //值
8           public int hash_coll; //哈希码
9       }
10      private bucket[] buckets; //存储哈希表数据的数组(数据桶)
11      private int count; //元素个数
12      private int loadsize; //当前允许存储的元素个数
13      private float loadFactor; //填充因子
14      //默认构造方法
15      public Hashtable() : this(0, 1.0f) { }
16      //指定容量的构造方法
17      public Hashtable(int capacity, float loadFactor)
18      {
19          if (!(loadFactor >= 0.1f && loadFactor <= 1.0f))
20              throw new ArgumentOutOfRangeException(
21                  "填充因子必须在0.1~1之间");
22          this.loadFactor = loadFactor > 0.72f ? 0.72f : loadFactor;
23          //根据容量计算表长
24          double rawsize = capacity / this.loadFactor;
25          int hashsize = (rawsize > 11) ? //表长为大于11的素数
26              HashHelpers.GetPrime((int)rawsize) : 11;
27          buckets = new bucket[hashsize]; //初始化容器
28          loadsize = (int)(this.loadFactor * hashsize);
29      }
30      public virtual void Add(Object key, Object value) //添加
31      {   
32          Insert(key, value, true);
33      }
34      //哈希码初始化
35      private uint InitHash(Object key,int hashsize,
36          out uint seed,out uint incr)
37      {
38          uint hashcode = (uint)GetHash(key) & 0x7FFFFFFF; //取绝对值
39          seed = (uint)hashcode; //h1
40          incr = (uint)(1 + (((seed >> 5)+1) % ((uint)hashsize-1)));//h2
41          return hashcode; //返回哈希码
42      }
43      public virtual Object this[Object key] //索引器
44      {
45          get
46          {
47              uint seed; //h1
48              uint incr; //h2
49              uint hashcode = InitHash(key, buckets.Length, 
50                  out seed, out incr);
51              int ntry = 0; //用于表示h(key,i)中的i值
52              bucket b;
53              int bn = (int)(seed % (uint)buckets.Length); //h(key,0)
54              do
55              {
56                  b = buckets[bn]; 
57                  if (b.key == null) //b为无冲突空位时
58                  {  //找不到相应的键,返回空
59                      return null;
60                  }
61                  if (((b.hash_coll & 0x7FFFFFFF) == hashcode) &&
62                      KeyEquals(b.key, key))
63                  {   //查找成功
64                      return b.val;
65                  }
66                  bn = (int)(((long)bn + incr) % 
67                      (uint)buckets.Length); //h(key+i)
68              } while (b.hash_coll < 0 && ++ntry < buckets.Length);
69              return null;
70          }
71          set
72          {
73              Insert(key, value, false);
74          }
75      }
76      private void expand() //扩容
77      {   //使新的容量为旧容量的近似两倍
78          int rawsize = HashHelpers.GetPrime(buckets.Length * 2); 
79          rehash(rawsize);
80      }
81      private void rehash(int newsize) //按新容量扩容
82      {
83          bucket[] newBuckets = new bucket[newsize];
84          for (int nb = 0; nb < buckets.Length; nb++)
85          {
86              bucket oldb = buckets[nb];
87              if ((oldb.key != null) && (oldb.key != buckets))
88              {
89                  putEntry(newBuckets, oldb.key, oldb.val, 
90                      oldb.hash_coll & 0x7FFFFFFF);
91              }
92          }
93          buckets = newBuckets;
94          loadsize = (int)(loadFactor * newsize);
95          return;
96      }
97      //在新数组内添加旧数组的一个元素
98      private void putEntry(bucket[] newBuckets, Object key, 
99          Object nvalue, int hashcode)
100     {
101         uint seed = (uint)hashcode; //h1
102         uint incr = (uint)(1 + (((seed >> 5) + 1) % 
103             ((uint)newBuckets.Length - 1))); //h2
104         int bn = (int)(seed % (uint)newBuckets.Length);//哈希地址
105         do
106         {   //当前位置为有冲突空位或无冲突空位时都可添加新元素
107             if ((newBuckets[bn].key == null) || 
108                 (newBuckets[bn].key == buckets))
109             {   //赋值
110                 newBuckets[bn].val = nvalue;
111                 newBuckets[bn].key = key;
112                 newBuckets[bn].hash_coll |= hashcode;
113                 return;
114             }
115             //当前位置已存在其他元素时
116             if (newBuckets[bn].hash_coll >= 0)
117             {   //置hash_coll的高位为1
118                 newBuckets[bn].hash_coll |= 
119                     unchecked((int)0x80000000);
120             }
121             //二度哈希h1(key)+h2(key)
122             bn = (int)(((long)bn + incr) % (uint)newBuckets.Length);
123         } while (true);
124     }
125     protected virtual int GetHash(Object key)
126     {   //获取哈希码
127         return key.GetHashCode();
128     }
129     protected virtual bool KeyEquals(Object item, Object key)
130     {   //用于判断两key是否相等
131         return item == null ? false : item.Equals(key);
132     }
133     //当add为true时用作添加元素,当add为false时用作修改元素值
134     private void Insert(Object key, Object nvalue, bool add)
135     {   //如果超过允许存放元素个数的上限则扩容
136         if (count >= loadsize)
137         {   
138             expand();
139         }
140         uint seed; //h1
141         uint incr; //h2
142         uint hashcode = InitHash(key, buckets.Length,out seed, out incr);
143         int ntry = 0; //用于表示h(key,i)中的i值
144         int emptySlotNumber = -1; //用于记录空位
145         int bn = (int)(seed % (uint)buckets.Length); //索引号
146         do
147         {   //如果是有冲突空位,需继续向后查找以确定是否存在相同的键
148             if (emptySlotNumber == -1 && (buckets[bn].key == buckets) &&
149                 (buckets[bn].hash_coll < 0))
150             {
151                 emptySlotNumber = bn;
152             }
153             if (buckets[bn].key == null) //确定没有重复键才添加
154             {
155                 if (emptySlotNumber != -1) //使用之前的空位
156                     bn = emptySlotNumber;
157                 buckets[bn].val = nvalue;
158                 buckets[bn].key = key;
159                 buckets[bn].hash_coll |= (int)hashcode;
160                 count++;
161                 return;
162             }
163             //找到重复键
164             if (((buckets[bn].hash_coll & 0x7FFFFFFF)==hashcode) &&
165                 KeyEquals(buckets[bn].key, key))
166             {   //如果处于添加元素状态,则由于出现重复键而报错
167                 if (add)
168                 {
169                     throw new ArgumentException("添加了重复的键值!");
170                 }
171                 buckets[bn].val = nvalue; //修改批定键的元素
172                 return;
173             }
174             //存在冲突则置hash_coll的最高位为1
175             if (emptySlotNumber == -1)
176             {
177                 if (buckets[bn].hash_coll >= 0)
178                 {
179                     buckets[bn].hash_coll |= unchecked((int)0x80000000);
180                 }
181             }
182             bn = (int)(((long)bn + incr) % (uint)buckets.Length);//二度哈希
183         } while (++ntry < buckets.Length);
184         throw new InvalidOperationException("添加失败!");
185     }
186     public virtual void Remove(Object key) //移除一个元素
187     {
188         uint seed; //h1
189         uint incr; //h2
190         uint hashcode = InitHash(key, buckets.Length,out seed, out incr);
191         int ntry = 0; //h(key,i)中的i
192         bucket b;
193         int bn = (int)(seed % (uint)buckets.Length); //哈希地址
194         do
195         {
196             b = buckets[bn];
197             if (((b.hash_coll & 0x7FFFFFFF) == hashcode) &&
198                 KeyEquals(b.key, key)) //如果找到相应的键值
199             {   //保留最高位,其余清0
200                 buckets[bn].hash_coll &= unchecked((int)0x80000000);
201                 if (buckets[bn].hash_coll != 0) //如果原来存在冲突
202                 {   //使key指向buckets
203                     buckets[bn].key = buckets;
204                 }
205                 else //原来不存在冲突
206                 {   //置key为空
207                     buckets[bn].key = null;
208                 }
209                 buckets[bn].val = null;  //释放相应的“值”。
210                 count--;
211                 return;
212             } //二度哈希
213             bn = (int)(((long)bn + incr) % (uint)buckets.Length);
214         } while (b.hash_coll < 0 && ++ntry < buckets.Length);
215     }
216     public override string ToString()
217     {
218         string s = string.Empty;
219         for (int i = 0; i < buckets.Length; i++)
220         {
221             if (buckets[i].key != null && buckets[i].key != buckets)
222             {   //不为空位时打印索引、键、值、hash_coll
223                 s += string.Format("{0,-5}{1,-8}{2,-8}{3,-8}rn",
224                     i.ToString(), buckets[i].key.ToString(),
225                     buckets[i].val.ToString(), 
226                     buckets[i].hash_coll.ToString());
227             }
228             else
229             {   //是空位时则打印索引和hash_coll
230                 s += string.Format("{0,-21}{1,-8}rn", i.ToString(),
231                     buckets[i].hash_coll.ToString());
232             }
233         }
234         return s;
235     }
236     public virtual int Count //属性
237     {   //获取元素个数
238         get { return count; }
239     }
240 }

C# 哈希表的实现的更多相关文章

  1. [PHP内核探索]PHP中的哈希表

    在PHP内核中,其中一个很重要的数据结构就是HashTable.我们常用的数组,在内核中就是用HashTable来实现.那么,PHP的HashTable是怎么实现的呢?最近在看HashTable的数据 ...

  2. Java 哈希表运用-LeetCode 1 Two Sum

    Given an array of integers, find two numbers such that they add up to a specific target number. The ...

  3. ELF Format 笔记(十五)—— 符号哈希表

    ilocker:关注 Android 安全(新手) QQ: 2597294287 符号哈希表用于支援符号表的访问,能够提高符号搜索速度. 下表用于解释该哈希表的组织,但该格式并不属于 ELF 规范. ...

  4. Java基础知识笔记(一:修饰词、向量、哈希表)

    一.Java语言的特点(养成经常查看Java在线帮助文档的习惯) (1)简单性:Java语言是在C和C++计算机语言的基础上进行简化和改进的一种新型计算机语言.它去掉了C和C++最难正确应用的指针和最 ...

  5. 什么叫哈希表(Hash Table)

    散列表(也叫哈希表),是根据关键码值直接进行访问的数据结构,也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做散列函数,存放记录的数组叫做散列表. - 数据结构 ...

  6. 【哈希表】CodeVs1230元素查找

    一.写在前面 哈希表(Hash Table),又称散列表,是一种可以快速处理插入和查询操作的数据结构.哈希表体现着函数映射的思想,它将数据与其存储位置通过某种函数联系起来,其在查询时的高效性也体现在这 ...

  7. openssl lhash 数据结构哈希表

    哈希表是一种数据结构,通过在记录的存储位置和它的关键字之间建立确定的对应关系,来快速查询表中的数据: openssl lhash.h 为我们提供了哈希表OPENSSL_LHASH 的相关接口,我们可以 ...

  8. Berkeley DB的数据存储结构——哈希表(Hash Table)、B树(BTree)、队列(Queue)、记录号(Recno)

    Berkeley DB的数据存储结构 BDB支持四种数据存储结构及相应算法,官方称为访问方法(Access Method),分别是哈希表(Hash Table).B树(BTree).队列(Queue) ...

  9. python数据结构与算法——哈希表

    哈希表 学习笔记 参考翻译自:<复杂性思考> 及对应的online版本:http://greenteapress.com/complexity/html/thinkcomplexity00 ...

  10. [转]:Delphi 中的哈希表(1): THashedStringList

    unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms ...

随机推荐

  1. txt无法正常保存正文的解决办法

    最近遇到一个问题,txt文档中写了中文,则保存的时候 就会提示“该文件含有unicode格式字符,当文件保存为ANST编码文本文件时,该字符将会丢失”.虽然有解决办法,但不彻底,用起来总是很费劲,研究 ...

  2. RMQ问题与ST算法

    RMQ(Range Minimum/Maximum Query)问题是求区间最值问题. 对于长度为 n 的数组 A,进行若干次查询,对于区间 [L,R] 返回数组A中下标在 [L,R] 中的最小(大) ...

  3. Spring AOP体系学习总结:

    二.Spring AOP体系学习总结: 要理解AOP整体的逻辑需要理解一下Advice,Pointcut,Advisor的概念以及他们的关系. Advice是为Spring Bean提供增强逻辑的接口 ...

  4. Codeforces Round #310 (Div. 2)--A(简单题)

    http://codeforces.com/problemset/problem/556/A 题意:给一个01字符串,把所有相邻的0和1去掉,问还剩下几个0和1. 题解:统计所有的0有多少个,1有多少 ...

  5. IE8下网页中的视频会遮挡住顶层DIV的解决办法

    在IE8浏览器下,发现网页中的视频会遮挡住本来固定在最顶层的DIV.即便使用z-index也无法解决.但是其他浏览器是正常的. 解决的办法很简单,就是在调用flash视频播放器的时候,加上一个参数“o ...

  6. javascript创建对象的7种方式

    /*1.工厂模式*/ function createPerson(name,age,job) { var o = new object(); o.name = name; o.age = age; o ...

  7. 消息中间件MQ基础理论知识

    欢迎转载,转载请注明出处:http://www.cnblogs.com/lidabnu/p/5723280.html 消息中间件已经流行很长时间,一般情况下,不需要自己来从头研发.设计消息中间件,所以 ...

  8. 解读zookeeper的配置项

    zookeeper的默认配置文件为zookeeper/conf/zoo_sample.cfg,需要将其修改为zoo.cfg.其中各配置项的含义,解释如下: 1.tickTime:CS通信心跳数 Zoo ...

  9. 数据库学习(整理)----6--Oracle如何快速备份和多次备份数表数据

    1.说明:  这里假设一种应用场景! 假设,银行系统中有大量的数据需要及时备份,如何才能快速高效呢! 条件需求: (1).不能设置同步锁(设置的会影响银行正常业务进行!使得银行系统处于维护状态,这是不 ...

  10. Codeforces 553D Nudist Beach(图论,贪心)

    Solution: 假设已经选了所有的点. 如果从中删掉一个点,那么其它所有点的分值只可能减少或者不变. 如果要使若干步删除后最小的分值变大,那么删掉的点集中肯定要包含当前分值最小的点. 所以每次删掉 ...