背景
Java 和 .Net 平台都有一个所有引用类型都会间接或直接继承的类型:Object,这个类型提供最基本的相等性比较算法和哈希算法,很多书上都给出了在重写这两个算法的时候的主意事项,其中大多数主意事项都和哈希表有关。
《CLR VIA C#》的作者觉得将哈希算法放到 Object 不是很适合,我也有这种感觉,每次重写相等性比较算法都要重新哈希算法,非常不爽,我就没打算将其用到哈希表中作为键使用。
哈希表
定义
使用哈希算法将 A 数值空间(键)映射到 B 数值空间(存储),如下:
A -> 0
B -> 1
C -> 2
D -> 3
B 数值空间的查询速度要求非常快,毫无疑问就是数值了。
冲突
数组的大小是固定,当出现冲突(两个键映射为同一个数值了)的时候如何处理呢?有两种策略:
- 在数组中按照一定的算法继续探测其它地址。
- 在数组的每个元素存储的都是链表,冲突的元素会被插入到链表中。
实现
链表节点
1 class Node<TKey, TValue> 2 { 3 public TKey Key { get; set; } 4 5 public TValue Value { get; set; } 6 7 public Node<TKey, TValue> Next { get; set; } 8 }
链表
1 class LinkedList<TKey, TValue> 2 { 3 public Node<TKey, TValue> First { get; set; } 4 5 public void Insert(TKey key, TValue value) 6 { 7 if (this.Contains(key)) 8 { 9 throw new InvalidOperationException("不能插入重复的键"); 10 } 11 12 var node = new Node<TKey, TValue> { Key = key, Value = value }; 13 node.Next = this.First; 14 this.First = node; 15 } 16 17 public void Delete(TKey key) 18 { 19 Node<TKey, TValue> parent; 20 Node<TKey, TValue> node; 21 22 if (!this.Find(key, out parent, out node)) 23 { 24 throw new InvalidOperationException("不存在指定的键"); 25 } 26 27 if (parent == null) 28 { 29 this.First = null; 30 } 31 else 32 { 33 parent.Next = node.Next; 34 } 35 } 36 37 public bool Contains(TKey key) 38 { 39 Node<TKey, TValue> parent; 40 Node<TKey, TValue> node; 41 42 return this.Find(key, out parent, out node); 43 } 44 45 public bool Find(TKey key, out Node<TKey, TValue> node) 46 { 47 Node<TKey, TValue> parent; 48 49 return this.Find(key, out parent, out node); 50 } 51 52 public bool Find(TKey key, out Node<TKey, TValue> parent, out Node<TKey, TValue> node) 53 { 54 parent = null; 55 node = this.First; 56 57 while (node != null) 58 { 59 if (node.Key.Equals(key)) 60 { 61 return true; 62 } 63 64 parent = node; 65 node = node.Next; 66 } 67 68 return false; 69 } 70 }
哈希表
1 class HashTable<TKey, TValue> 2 { 3 private LinkedList<TKey, TValue>[] _items; 4 5 public HashTable(int size) 6 { 7 _items = new LinkedList<TKey, TValue>[size]; 8 } 9 10 public void Insert(TKey key, TValue value) 11 { 12 var index = this.HashToIndex(key); 13 if (_items[index] == null) 14 { 15 _items[index] = new LinkedList<TKey, TValue>(); 16 } 17 18 _items[index].Insert(key, value); 19 } 20 21 public void Delete(TKey key) 22 { 23 var index = this.HashToIndex(key); 24 if (_items[index] == null) 25 { 26 throw new InvalidOperationException("不存在指定的键"); 27 } 28 29 _items[index].Delete(key); 30 } 31 32 public bool Find(TKey key, out TValue value) 33 { 34 value = default(TValue); 35 36 var index = this.HashToIndex(key); 37 if (_items[index] == null) 38 { 39 return false; 40 } 41 42 Node<TKey, TValue> node; 43 var finded = _items[index].Find(key, out node); 44 if (!finded) 45 { 46 return false; 47 } 48 else 49 { 50 value = node.Value; 51 } 52 53 return true; 54 } 55 56 private int HashToIndex(TKey key) 57 { 58 return key.GetHashCode() % _items.Length; 59 } 60 }
备注
哈希算法、负载因子、自动扩容都是需要考虑的,这里就不多说了。