Hash Table原理、基本使用方法、及源码分析
来源:互联网 发布:视频剪辑专业软件 编辑:程序博客网 时间:2024/06/06 03:26
一.Hash Table简介
Hash Table,哈希表,作为Hash Map的一个兄弟,除了例如线程安全以及一些特性的有无的区别,它们的构造、原理及作用都很相似。由于我使用的是C++,因此我更加注重于Hash Table在C++中的是对应着Dictionary的子类,也就是一类有着Key-Value映射的对象。
就比如说,我们想要查找字典里对于“Table”一词的释义时,我们只需要直接翻到印着“Table”一词的那一页找到这个词就可以马上看到它的意思(当然手上能有一份关于哪些词在哪一页的表是最好的)。
但是,它又被叫做“散列表数组”,因为它同时具有着数组与链表的特征,每个元素具有索引(由Hash Function生成),也具有指向下一个的地址指针。(将在第二部分详细说明。)
这些特性就直接赋予了它在数据结构中独一无二的地位与作用。优点:查找速度平均为O(1),能够快速找到我们需要的那个元素,而不像是对待数组一样我们需要遍历整个数组才可以。但是它同时牺牲了空间存储,它的写入、修改相对会比较麻烦。因此,Hash Table适用于读取操作多而写入操作少且存储的为key-value的object的场合。
接下来,就来看一下Hash Table的工作原理。
二.Hash Table的原理
首先,先上一幅图来直观地理解哈希表的结构。
我们可以看到,左边第一列是Hash Table的主体,它的每一个元素实际上是一个Entry数组(实际上为单向链表),我们更加乐意称其为“Hash Bucket”,因为这个名字更能够形象地展示它将多个符合同一个Hash Function的元素放在一起的情形。这个时候我们就会觉得奇怪,为什么一个位置下可以放下多个东西呢?而且为什么同一个位置下的东西居然会有如同链表般从一个节点指向下一个节点的构造呢?
这个时候就要先引入Hash Function了。之所以会出现多个物体被塞入一个位置上的情况,是因为Hash Function对它们的Key求得的Hash Code意外的相同,使得它们在主体数组中的索引位置一致。
让我们来做一个比较贴近生活的比喻。现在有七个货物,上面的编号分别是4,5,7,10,14,17,21,各自的货物都不相同,可是我们只有四个货仓编号为0,1,2,3,要如何将这几个货物放进货仓而且当我们需要拿货物时可以最快地找出来呢?
这个时候有个叫“汉德森迪克”(滑稽)的人提议说,不如根据货物编号除以4的余数来存放货物吧!到时候要拿的时候只要自己口算一下就好啦!我们都觉得这个方法好,于是我们就将货物分别搬到了对应的货仓内并为了方便,将新货物靠着放好。于是我们得到了:
这一个生活模型就很好解决了当我们面临一堆无序的数据时如何处理他们当我们在之后需要快速查找他们的问题。当我们在每一个货物上贴上一个小标签写着下个货物在哪个货架时,这一切看起来就更像是链表与数组的组合了。
所以转换到我们对数据进行处理时的情景中,我们就会根据哈希函数对所有数值进行一次运算,并根据运算结果将它们放入各自的数组位置上,当一个位置上有多个元素时,则会自动将这些数字按照入栈的次序生成链表。
因此,我们就可以很好地理解Hash Table中包含的基本元素与特性以及一些术语了。
1.初始容器容量:也就是Hash Bucket——仓库的数量
2.加载因子:是一个float类型的大小自0至1的数,当已占用量占到总容量 * 加载因子(即容器的阈值 threshold)时,Hash Table会自动rehash,也就是其会自动将长度扩展至原长度的2倍 + 1,(C++中扩增为大于原长度两倍的最小素数,java中则为原长度的2倍+1。)同时还会重新计算各个元素存放的索引位置。因为常用的索引计算方式为 索引 = 各元素的key % 容量 。默认的加载因子为0.75。
3.modCount:记录Hash Table被修改的次数,是用来实现 fail-fast 机制的。这个不为本文重点。
4.冲突:就是指经过Hash Function后求得的Hash Code(索引)相同的多个元素不得不放在同一个位置上的状况。在我们的理想状况中,我们当然希望每个位置只有一个元素,这样直接查找Key的Hash Code即可得到该元素。但是我们在复杂情景下不得不重视这一种冲突带来的问题,因此我们便利用多个在同一位置的元素组成链表的方法来解决冲突。当我们在遍历一个位置上的元素时,则通过指针的变化来遍历即可。(这一种方法又称作开链法)
用C++实现开链法的例子:C++实现哈希表
看完原理,接下来介绍一下哈希表的一些基本操作。
三.Hash Table的基本使用
1.Hash Table的构造函数
由于这一结构也是来自Java,因此我就用java来写一下。(匆忙自学了一下,若有写错请指出)
Java.util.Hashtable提供了四种方法让用户构造哈希表,前三种分别是:
public Hashtable() public Hashtable(int initialcapacity) public Hashtable(int initialCapacity,float loadFactor) //ininitialcapacity为初始容量,loadFactor为加载因子
第四种则是将另一个映射map t 添加到原来的Hash Table中
public Hashtable.putAll(Map<? extends K, ? extends V> t)//map t的key需是K类或者其子类的对象,value需是类V或者其子类的对象
若觉得难理解,则看看实例(用了Hash Map来写):
import java.util.HashMap;public class Main { public static void main(String args[]) { HashMap<Integer, String> Star2 = new HashMap<Integer, String>(); HashMap<Integer, String> Star1 = new HashMap<Integer, String>(); Star1.put(1, "Orion"); Star1.put(2, "The"); Star1.put(3, "Star");//将指定的key-Value映射元素放入了Hash Map中 System.out.println("Values in Star1: "+ Star1);//将Star1中的元素放入2中 Star2.putAll(Star1); System.out.println("Values in Star2: "+ star2); } }
运行结果 :
Values in Star1: 1=Orion,2=The,3=StarValues in Star2: 1=Orion,2=The,3=Star
2.Hash Table的查找,插入,删除,获取。
Hashtable.put(key); //在哈希表中添加新的key-value对Hashtable.remove(key); //在哈希表中去除某个key-value对Hashtable.clear(); //清除哈希表内所有元素Hashtable.contains(value);//判断哈希表内是否含有某个valueHashtable.containsKey(key)//判断哈希表内是否含有某个keyHashtable.get(key)//获取哈希表内key对应的value
各种方法的源码请看下文源码分析
四.源码分析
package java.util; import java.io.*; public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, java.io.Serializable { private transient Entry[] table; //由于此处采用开链法解决冲突问题,故设置每个元素为Entry数组 private transient int count; //设置哈希表容量count private int threshold; //哈希表的阈值(= 容量 * 加载因子) private float loadFactor; //// 哈希表的加载因子 private transient int modCount = 0; //哈希表的modCount负责记录Hashtable被修改的次数 private static final long serialVersionUID = 1421746759512286392L; //申请一个已设定初始容量与加载因子的哈希表 public Hashtable(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); //若初始容量小于0则抛出异常 if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal Load: "+loadFactor); //对加载因子的非负性及浮点类型进行判断 if (initialCapacity==0) initialCapacity = 1; this.loadFactor = loadFactor; //java中this表示这个类的属性 table = new Entry[initialCapacity]; //申请新的数组 threshold = (int)(initialCapacity * loadFactor); } public Hashtable(int initialCapacity) { this(initialCapacity, 0.75f); } //当用户申请一个只定义长度的哈希表,则加载因子默认为0.75 public Hashtable() { this(11, 0.75f); } //申请默认的哈希表,默认长度为11,默认加载因子为0.75 public Hashtable(Map<? extends K, ? extends V> t) { this(Math.max(2*t.size(), 11), 0.75f); putAll(t); } //putAll将满足映射<K,V>的map t加到了Hashtable中 public synchronized int size() { return count; } //synchronized是保护hashtable线程安全的函数,它会将有线程正在修改的哈希表进行lock来保护读取 public synchronized boolean isEmpty() { return count == 0; } public synchronized Enumeration<K> keys() { return this.<K>getEnumeration(KEYS); } //Enumeration表示一个枚举变量,返回所有key public synchronized Enumeration<V> elements() { return this.<V>getEnumeration(VALUES); } //同理得,返回所有value // 判断哈希表中是否含有某一个value public synchronized boolean contains(Object value) { if (value == null) { throw new NullPointerException(); } //哈希表的值不可为0 // 从后向前遍历table数组中的元素(Entry) // 对于每个Entry(单向链表),逐个遍历,判断节点的值是否等于value Entry tab[] = table; for (int i = tab.length ; i-- > 0 ;) { for (Entry<K,V> e = tab[i] ; e != null ; e = e.next) //在一个位置上对链表的元素进行遍历检查,e为链表元素,e.next为上一项指向下一项的指针域 { if (e.value.equals(value)) //equal函数判断是否相等 { return true; } } } return false; } public boolean containsValue(Object value) { return contains(value); } //containsValue()与contains()相同 // 判断Hashtable是否包含某个key public synchronized boolean containsKey(Object key) { Entry tab[] = table; int hash = key.hashCode(); //用key的hashcode代替hash Function计算后的hash值 int index = (hash & 0x7FFFFFFF) % tab.length; //通过位运算求其在数组的索引 for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) { //用同样映射的数组e来表示所求元素所在的Hash Bucket,再同理遍历链表 if ((e.hash == hash) && e.key.equals(key)) { return true; } } return false; } // 寻找key在哈希表内对应的value,若无则返回Null public synchronized V get(Object key) { Entry tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; //通过更高效的位运算来求索引 for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { return e.value; } } return null; } //rehash过程,扩大长度并重新计算各Entry的索引 protected void rehash() { int oldCapacity = table.length; Entry[] oldMap = table; //生成一个原哈希表的浅表副本 int newCapacity = oldCapacity * 2 + 1;//扩大长度 Entry[] newMap = new Entry[newCapacity];//申请新map modCount++; //记录哈希表被修改的次数 threshold = (int)(newCapacity * loadFactor); table = newMap;//将table清空并替换为新的哈希map //将“旧的Hashtable”中的元素复制到“新的Hashtable”中 for (int i = oldCapacity ; i-- > 0 ;) { for (Entry<K,V> old = oldMap[i] ; old != null ; ) { Entry<K,V> e = old;//e记录当前位置,以便old指向链表中的下一个 old = old.next;//若old为空指针时,此处会异常。 //通过求余重新计算index int index = (e.hash & 0x7FFFFFFF) % newCapacity; e.next = newMap[index];//将e的下一个元素也定位于此位置 newMap[index] = e;//再用e替换掉新哈希表的当前位置元素 } } } //由此可以看出当加载因子过大时,则哈希表在多数据存储时越满,冲突情况会越多,rehash虽然需要耗费时间,但是其却解决了冲突情况多引起的哈希表退化为多个链表带来的效率下降问题。 // 将新的key-value对添加到Hashtable中 public synchronized V put(K key, V value) { if (value == null) { throw new NullPointerException(); } //hashtable中不允许value为null // 若“Hashtable中已存在键为key的键值对”, // 则用“新的value”替换“旧的value” Entry tab[] = table; int hash = key.hashCode();//先用第三变量存储当前key的哈希码 int index = (hash & 0x7FFFFFFF) % tab.length;//计算其所在索引 for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { V old = e.value; e.value = value; return old; } } //哈希表中一个元素有hash属性即哈希码,其是对key进行再一次的Hash Function运算得出。但是索引是进一步由hash进行求模。V为value的类 modCount++; //当不存在时则只使修改值+1 if (count >= threshold) { rehash(); tab = table; index = (hash & 0x7FFFFFFF) % tab.length; } //当实际占用大于阈值时,则开始rehash。 Entry<K,V> e = tab[index]; tab[index] = new Entry<K,V>(hash, key, value, e); count++; return null; } //当以上情况不满足时则在索引处添加key-value对 // 删除Hashtable中键为key的元素 public synchronized V remove(Object key) { Entry tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; //求出其索引 //因为是单链表,因此要保留带删节点的前一个节点,才能有效地删除节点 for (Entry<K,V> e = tab[index], prev = null ; e != null ; prev = e, e = e.next) //prev用来保存被删除节点的前一个节点 { if ((e.hash == hash) && e.key.equals(key)) { modCount++; if (prev != null) { prev.next = e.next;//被删除后e的后一个则变为prev的后一个(原为prev的后两个) } else { tab[index] = e.next; } count--; V oldValue = e.value; //进行旧值赋值 e.value = null; //然后清空原值 return oldValue; } } return null; } // 将有着<K,V>映射的map t的全部元素添加到Hashtable中 public synchronized void putAll(Map<? extends K, ? extends V> t) { for (Map.Entry<? extends K, ? extends V> e : t.entrySet()) put(e.getKey(), e.getValue()); //调用了哈希表中的put和get函数 } // 清空Hashtable中的所有key-value public synchronized void clear() { Entry tab[] = table; modCount++; for (int index = tab.length; --index >= 0; ) tab[index] = null; count = 0; } // 克隆一个Hashtable public synchronized Object clone() { try { Hashtable<K,V> t = (Hashtable<K,V>) super.clone(); t.table = new Entry[table.length];//创建新哈希表 for (int i = table.length ; i-- > 0 ; ) { t.table[i] = (table[i] != null) ? (Entry<K,V>) table[i].clone() : null; } t.keySet = null; t.entrySet = null; t.values = null; t.modCount = 0; //将新的哈希表初始化 return t; } catch (CloneNotSupportedException e) { throw new InternalError(); } } //java中的toString方法,以字符串形式返回数据 public synchronized String toString() { int max = size() - 1; if (max == -1) return "{}"; StringBuilder sb = new StringBuilder(); Iterator<Map.Entry<K,V>> it = entrySet().iterator(); //it为一个迭代变量 sb.append('{'); //在sb中加入“{” for (int i = 0; ; i++) { Map.Entry<K,V> e = it.next(); K key = e.getKey(); V value = e.getValue(); //获取key与value sb.append(key == this ? "(this Map)" : key.toString()); sb.append('='); sb.append(value == this ? "(this Map)" : value.toString()); //对value与Key进行判断并赋予相应的值 if (i == max) return sb.append('}').toString(); sb.append(", "); } } // 获取Hashtable的所有枚举类对象 private <T> Enumeration<T> getEnumeration(int type) { if (count == 0) { return (Enumeration<T>)emptyEnumerator; } //若长度为0即无元素实例对象,则报错 else { return new Enumerator<T>(type, false); } //对元素进行枚举直至false } // 获取Hashtable的迭代器,通常返回正常的Enumerator的对象。(Enumerator实现了迭代器和枚举两个接口) private <T> Iterator<T> getIterator(int type) { if (count == 0) { return (Iterator<T>) emptyIterator; } else { return new Enumerator<T>(type, true); } } // 设定Hashtable仅为一个key的集合,没有重复元素 private transient volatile Set<K> keySet = null; // 设定Hashtable是一个key-value映射的集合,没有重复元素 private transient volatile Set<Map.Entry<K,V>> entrySet = null; //前两者为java中的Set,不可重复。java中的list则可重复,Collcetion则为list子类 (详情看JDK源码) //设定Hashtable为一个key-value的集合,可以有重复元素 private transient volatile Collection<V> values = null; // 返回一个被synchronizedSet封装后的KeySet对象 // synchronizedSet封装的目的是对哈希表的所有元素进行lock来保证线程安全,即多线程运行时当有一个线程在修改时其余读取线程不应受到干扰 public Set<K> keySet() { if (keySet == null) keySet = Collections.synchronizedSet(new KeySet(), this); return keySet; } //keyset与AbstractSet均继承Set,故均无重复元素。此处为key的集合 private class KeySet extends AbstractSet<K> { public Iterator<K> iterator() { return getIterator(KEYS); } public int size() { return count; } //返回该类对象的长度的数值 public boolean contains(Object o) { return containsKey(o); } public boolean remove(Object o) { return Hashtable.this.remove(o) != null; } public void clear() { Hashtable.this.clear(); } } // 返回一个以<K,V>为映射的,被synchronizedSet封装后的EntrySet对象 public Set<Map.Entry<K,V>> entrySet() { if (entrySet==null) entrySet = Collections.synchronizedSet(new EntrySet(), this); return entrySet; } // Hashtable的Entry的Set集合。 // EntrySet继承于AbstractSet,所以,EntrySet中的元素没有重复的。 private class EntrySet extends AbstractSet<Map.Entry<K,V>> { public Iterator<Map.Entry<K,V>> iterator() { return getIterator(ENTRIES); } public boolean add(Map.Entry<K,V> o) { return super.add(o); } // 查找EntrySet中是否包含Object o public boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry entry = (Map.Entry)o; // 首先,在table中找到o对应的Entry链表 Object key = entry.getKey(); Entry[] tab = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; for (Entry e = tab[index]; e != null; e = e.next) //在链表中遍历寻找有无与object o相同的元素 if (e.hash==hash && e.equals(entry)) return true; return false; } // 删除元素Object(0)且返回bool值 public boolean remove(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<K,V> entry = (Map.Entry<K,V>) o; //将对象o所对应的的映射赋给entry K key = entry.getKey();//声明key变量 Entry[] tab = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; for (Entry<K,V> e = tab[index], prev = null; e != null; prev = e, e = e.next) { if (e.hash==hash && e.equals(entry)) { modCount++; if (prev != null) prev.next = e.next; else tab[index] = e.next; count--; e.value = null; return true; //成功删除后就会返回True值 } } return false; } public int size() { return count; } public void clear() { Hashtable.this.clear(); } } // 返回一个被synchronizedCollection封装后的ValueCollection对象 public Collection<V> values() { if (values==null) values = Collections.synchronizedCollection(new ValueCollection(), this); return values; } // Hashtable的value的Collection集合,ValueCollection继承于Collection,则元素可重复 private class ValueCollection extends AbstractCollection<V> { public Iterator<V> iterator() { return getIterator(VALUES); } public int size() { return count; } public boolean contains(Object o) { return containsValue(o); } public void clear() { Hashtable.this.clear(); } } // 构造equals()函数,若两个Hashtable的所有key-value键值对都相等,则判断它们两个相等 public synchronized boolean equals(Object o) { if (o == this) //理想状态下每个bucket只有一个元素 return true; if (!(o instanceof Map)) return false; Map<K,V> t = (Map<K,V>) o; if (t.size() != size()) return false; //判断两表长度是否相等 try { // 通过迭代器依次取出当前Hashtable的key-value键值对 // 并判断该键值对是否存在于Hashtable中。 // 若不存在,则立即返回false;否则,遍历完“当前Hashtable”并返回true。 Iterator<Map.Entry<K,V>> i = entrySet().iterator(); //将i设定为entry的迭代变量 while (i.hasNext()) { Map.Entry<K,V> e = i.next(); K key = e.getKey(); V value = e.getValue(); //进行key与value的赋值 if (value == null) { if (!(t.get(key)==null && t.containsKey(key))) //当t中确实含有key且其对应值为null时 return false; } else { if (!value.equals(t.get(key))) return false; } } } catch (ClassCastException unused) { return false; } catch (NullPointerException unused) { return false; } return true; } // 计算Entry的hashCode public synchronized int hashCode() { int h = 0; if (count == 0 || loadFactor < 0) return h; // 若 Hashtable的实际大小为0 或者 加载因子<0,则返回 h = 0。 loadFactor = -loadFactor; //使加载因子失效 Entry[] tab = table; for (int i = 0; i < tab.length; i++) for (Entry e = tab[i]; e != null; e = e.next) h += e.key.hashCode() ^ e.value.hashCode(); //h将为各key与value的hashcode的异或运算结果之和 loadFactor = -loadFactor; //恢复加载因子 return h; } // java.io.Serializable的写入函数 // 将Hashtable的“总的容量,实际容量,所有的Entry”都写入到输出流中 private synchronized void writeObject(java.io.ObjectOutputStream s) throws IOException { //输出哈希表的基本特征 s.defaultWriteObject(); //输出长度,计数器,以及每个key-value值 s.writeInt(table.length); s.writeInt(count); for (int index = table.length-1; index >= 0; index--) { Entry entry = table[index]; while (entry != null) { s.writeObject(entry.key); s.writeObject(entry.value); entry = entry.next; } } } // java.io.Serializable的读取函数:根据写入方式读出 // 将Hashtable的“总的容量,实际容量,所有的Entry”依次读出 private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { // Read in the length, threshold, and loadfactor s.defaultReadObject(); // Read the original length of the array and number of elements int origlength = s.readInt(); int elements = s.readInt(); // Compute new size with a bit of room 5% to grow but // no larger than the original size. Make the length // odd if it's large enough, this helps distribute the entries. // Guard against the length ending up zero, that's not valid. int length = (int)(elements * loadFactor) + (elements / 20) + 3; if (length > elements && (length & 1) == 0) length--; if (origlength > 0 && length > origlength) length = origlength; Entry[] table = new Entry[length]; count = 0; // Read the number of elements and then all the key/value objects for (; elements > 0; elements--) { K key = (K)s.readObject(); V value = (V)s.readObject(); // synch could be eliminated for performance reconstitutionPut(table, key, value); } this.table = table; } private void reconstitutionPut(Entry[] tab, K key, V value) throws StreamCorruptedException { if (value == null) { throw new java.io.StreamCorruptedException(); } // Makes sure the key is not already in the hashtable. // This should not happen in deserialized version. int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { throw new java.io.StreamCorruptedException(); } } // Creates the new entry. Entry<K,V> e = tab[index]; tab[index] = new Entry<K,V>(hash, key, value, e); count++; } private static class Entry<K,V> implements Map.Entry<K,V> { int hash; K key; V value; Entry<K,V> next;//声明一个指向下一个的Entry protected Entry(int hash, K key, V value, Entry<K,V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } protected Object clone() { return new Entry<K,V>(hash, key, value, (next==null ? null : (Entry<K,V>) next.clone())); } public K getKey() { return key; } public V getValue() { return value; } // 设置value。若value是null,则抛出异常。 public V setValue(V value) { if (value == null) throw new NullPointerException(); V oldValue = this.value; this.value = value; return oldValue; } // 覆盖equals()方法,判断两个Entry是否相等。 // 若两个Entry的key和value都相等,则认为它们相等。 public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry e = (Map.Entry)o; return (key==null ? e.getKey()==null : key.equals(e.getKey())) && (value==null ? e.getValue()==null : value.equals(e.getValue())); } public int hashCode() { return hash ^ (value==null ? 0 : value.hashCode()); } public String toString() { return key.toString()+"="+value.toString(); } } private static final int KEYS = 0; private static final int VALUES = 1; private static final int ENTRIES = 2; // Enumerator的作用是提供了“通过elements()遍历Hashtable的接口” 和 “通过entrySet()遍历Hashtable的接口”。 private class Enumerator<T> implements Enumeration<T>, Iterator<T> { // 指向Hashtable的table Entry[] table = Hashtable.this.table; // Hashtable的总的大小 int index = table.length; Entry<K,V> entry = null; Entry<K,V> lastReturned = null; int type; // Enumerator是 “迭代器(Iterator)” 还是 “枚举类(Enumeration)”的标志 // iterator为true,表示它是迭代器;否则,是枚举类。 boolean iterator; // 在将Enumerator当作迭代器使用时会用到,用来实现fail-fast机制。 fail-fast机制是为了保护线程安全 protected int expectedModCount = modCount; Enumerator(int type, boolean iterator) { this.type = type; this.iterator = iterator; } // 从遍历table的数组的末尾向前查找,直到找到不为null的Entry。 public boolean hasMoreElements() { Entry<K,V> e = entry; int i = index; Entry[] t = table; /* Use locals for faster loop iteration */ while (e == null && i > 0) { e = t[--i]; } entry = e; index = i; return e != null; } // 获取下一个元素 // 注意:从hasMoreElements() 和nextElement() 可以看出“Hashtable的elements()遍历方式” // 首先,从后向前的遍历table数组。table数组的每个节点都是一个单向链表(Entry)。 // 然后,依次向后遍历单向链表Entry。 public T nextElement() { Entry<K,V> et = entry; int i = index; Entry[] t = table; /* Use locals for faster loop iteration */ while (et == null && i > 0) { et = t[--i]; } entry = et; index = i; if (et != null) { Entry<K,V> e = lastReturned = entry; entry = e.next; return type == KEYS ? (T)e.key : (type == VALUES ? (T)e.value : (T)e); } throw new NoSuchElementException("Hashtable Enumerator"); } // 迭代器Iterator的判断是否存在下一个元素 // 实际上,它是调用的hasMoreElements() public boolean hasNext() { return hasMoreElements(); } // 迭代器获取下一个元素 // 实际上,它是调用的nextElement() public T next() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); return nextElement(); } // 迭代器的remove()接口。 // 首先,它在table数组中找出要删除元素所在的Entry, // 然后,删除单向链表Entry中的元素。 public void remove() { if (!iterator) throw new UnsupportedOperationException(); if (lastReturned == null) throw new IllegalStateException("Hashtable Enumerator"); if (modCount != expectedModCount) throw new ConcurrentModificationException(); synchronized(Hashtable.this) { Entry[] tab = Hashtable.this.table; int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length; for (Entry<K,V> e = tab[index], prev = null; e != null; prev = e, e = e.next) { if (e == lastReturned) { modCount++; expectedModCount++; if (prev == null) tab[index] = e.next; else prev.next = e.next; count--; lastReturned = null; return; } } throw new ConcurrentModificationException(); } } } private static Enumeration emptyEnumerator = new EmptyEnumerator(); private static Iterator emptyIterator = new EmptyIterator(); // 空枚举类 // 当Hashtable的实际大小为0;此时,又要通过Enumeration遍历Hashtable时,返回的是“空枚举类”的对象。 private static class EmptyEnumerator implements Enumeration<Object> { EmptyEnumerator() { } // 空枚举类的hasMoreElements() 始终返回false public boolean hasMoreElements() { return false; } // 空枚举类的nextElement() 抛出异常 public Object nextElement() { throw new NoSuchElementException("Hashtable Enumerator"); } } // 空迭代器 // 当Hashtable的实际大小为0;此时,又要通过迭代器遍历Hashtable时,返回的是“空迭代器”的对象。 private static class EmptyIterator implements Iterator<Object> { EmptyIterator() { } public boolean hasNext() { return false; } public Object next() { throw new NoSuchElementException("Hashtable Iterator"); } public void remove() { throw new IllegalStateException("Hashtable Iterator"); } } }
参考资料:
1.MSDN文档
2.JDK源码
- Hash Table原理、基本使用方法、及源码分析
- SSH原理及基本使用方法
- nginx 基本hash的初始化 源码分析
- Struts2原理及源码分析
- hash table原理与应用
- OAuth认证协议原理分析及使用方法
- OAuth认证协议原理分析及使用方法
- OAuth认证协议原理分析及使用方法
- OAuth认证协议原理分析及使用方法
- Oauth认证协议原理分析及使用方法
- OAuth认证协议原理分析及使用方法
- OAuth认证协议原理分析及使用方法
- OAuth认证协议原理分析及使用方法
- OAuth认证协议原理分析及使用方法
- OAuth认证协议原理分析及使用方法
- OAuth认证协议原理分析及使用方法
- OAuth认证协议原理分析及使用方法
- OAuth认证协议原理分析及使用方法
- DAY44 数据库--MySQL
- 2017.07.24 MyBatis基础
- vim列操作-替换
- 方差分析
- GitHub本地操作
- Hash Table原理、基本使用方法、及源码分析
- netty之SimpleChannelInboundHandler
- vim列操作-删除操作
- vim和shell交互两种方式
- 学习笔记TF030:实现AlexNet
- Nginx反向代理服务器安装与配置
- 拷贝两个文件夹中不同的内容到一个文件夹
- Mybatis基础
- 【Redis基础】事务与事件