java源码分析之集合框架HashTable 11

时间:2023-02-06 17:18:49

HashTable :

java源码分析之集合框架HashTable 11

  1. 此类实现一个哈希表,该哈希表将键映射到相应的值。任何非null 对象都可以用作键或值
  2. 为了成功地在哈希表中存储和获取对象,用作键的对象必须实现 hashCode 方法和equals 方法。
  3. Hashtable 的实例有两个参数影响其性能初始容量加载因子容量 是哈希表中 的数量,初始容量 就是哈希表创建时的容量。注意,哈希表的状态为open:在发生“哈希冲突”的情况下,单个桶会存储多个条目,这些条目必须按顺序搜索。加载因子 是对哈希表在其容量自动增加之前可以达到多满的一个尺度。初始容量和加载因子这两个参数只是对该实现的提示。关于何时以及是否调用 rehash 方法的具体细节则依赖于该实现。
  4. 通常,默认加载因子(0.75)在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查找某个条目的时间(在大多数Hashtable 操作中,包括getput 操作,都反映了这一点)。
  5. 初始容量主要控制空间消耗与执行 rehash 操作所需要的时间损耗之间的平衡。如果初始容量大于Hashtable 所包含的最大条目数除以加载因子,则永远 不会发生rehash 操作。但是,将初始容量设置太高可能会浪费空间。
  6. 如果很多条目要存储在一个 Hashtable 中,那么与根据需要执行自动 rehashing 操作来增大表的容量的做法相比,使用足够大的初始容量创建哈希表或许可以更有效地插入条目。(如果存储很多条目,相比HashTable自己再扩容,将初始容量设置大点更速度快)。
  7. HashTable是线程安全的(等会我们在源码中就能看出)




Hashtable 结构:(注意这里table首字母xiaoxi)

java源码分析之集合框架HashTable 11

public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable

  1. Hashtable不仅继承了Dictionary ,还实现了接口Serializable, Cloneable, Map<K,V> 。即HashTable序列化,可复制clone,是键值对保存值。
  2. Dictionary 类是任何可将键映射到相应值的类(如 Hashtable)的抽象父类。每个键和每个值都是一个对象。在任何一个 Dictionary 对象中,每个键至多与一个值相关联。给定一个 Dictionary 和一个键,就可以查找所关联的元素。任何非null 对象都可以用作键或值。通常,应该在此类的实现中使用equals 方法,以决定两个键是否相同。注:此类已过时。新的实现应该实现 Map 接口,而不是扩展此类.


Hashtable 方法:由源码可知HashTable是线程安全的。HashTable之所以是线程安全的,是因为方法上都加了synchronized关键字

synchronized void                clear()  
synchronized Object clone()
boolean contains(Object value)
synchronized boolean containsKey(Object key)
synchronized boolean containsValue(Object value)
synchronized Enumeration<V> elements()
synchronized Set<Entry<K, V>> entrySet()
synchronized boolean equals(Object object)
synchronized V get(Object key)
synchronized int hashCode()
synchronized boolean isEmpty()
synchronized Set<K> keySet()
synchronized Enumeration<K> keys()
synchronized V put(K key, V value)
synchronized void putAll(Map<? extends K, ? extends V> map)
synchronized V remove(Object key)
synchronized int size()
synchronized String toString()
synchronized Collection<V> values()



Hashtable 数据结构

存储结构:

和HashMap一样,HashTable内部也维护了一个数组,数组中存放的是Entry<K,V>实体,数组定义如下:

private transient Entry<K,V>[] table;


Hashtable逻辑结构:

java源码分析之集合框架HashTable 11



Entry 实体:(相比HashMap的Entry,多写了一个clone()方法)

    /** 
* Entry实体类的定义
*/
private static class Entry<K,V> implements Map.Entry<K,V> {
int hash;//哈希值
final 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;
}

//这里是单个Entry复制,深复制
protected Object clone() {
return new Entry<>(hash, key, value,
(next==null ? null : (Entry<K,V>) next.clone()));
}

// 重写Map.Entry的全部方法5个

public K getKey() {
return key;
}

public V getValue() {
return value;
}

public V setValue(V value) {
if (value == null)
throw new NullPointerException();

V oldValue = this.value;
this.value = value;
return oldValue;
}

//判断两个Entry是否相等
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry)o;
//必须两个Entry的key和value均相等才行
return key.equals(e.getKey()) && value.equals(e.getValue());
}

public int hashCode() {
return (Objects.hashCode(key) ^ Objects.hashCode(value));//异或
}

public String toString() {//重写Object类的toString方法
return key.toString()+"="+value.toString();
}
}



Hashtable属性:

	private transient Entry<K,V>[] table;

private transient int count;//记录HashTable中有多少Entry实体

//阈值,用于判断是否需要调整Hashtable的容量(threshold = 容量*加载因子)
private int threshold;

// 加载因子
private float loadFactor;

// Hashtable被改变的次数,用于fail-fast
private transient int modCount = 0;

// 序列版本号
private static final long serialVersionUID = 1421746759512286392L;

//最大的阈值,不能超过这个
static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;

//哈希种子,使哈希表分布更均匀
transient int hashSeed;

//最大容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

//set视图中需要的属性,具体看下面视图方法源码
private transient volatile Set<K> keySet = null;
private transient volatile Set<Map.Entry<K,V>> entrySet = null;
private transient volatile Collection<V> values = null;
private static final int KEYS = 0;
private static final int VALUES = 1;
private static final int ENTRIES = 2;






构造方法:

 public Hashtable(int initialCapacity, float loadFactor) {
//判断是否是正常数值
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
//判断是否是正常数值
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal Load: "+loadFactor);

if (initialCapacity==0)
initialCapacity = 1;
this.loadFactor = loadFactor;
table = new Entry[initialCapacity];//初始化数组
//初始化阕值 = 容量 * 加载因子
threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
initHashSeedAsNeeded(initialCapacity);//初始化哈希种子
}


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) {
//如果Map的2倍容量大于11,则使用新的容量
this(Math.max(2*t.size(), 11), 0.75f);
putAll(t);
}


存取方法:

put(K key, V value) 

put(K key, V value)
          将指定 key 映射到此哈希表中的指定 value

    public synchronized V put(K key, V value) {
// 确保value不为空
if (value == null) {
throw new NullPointerException();
}

// Makes sure the key is not already in the hashtable.
Entry tab[] = table;
int hash = hash(key);//计算hash值,此处会检测key是否为空
int index = (hash & 0x7FFFFFFF) % tab.length;//计算出值在数组table中的位置

for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {//如果对应的key已经存在
V old = e.value;
e.value = value;//替换掉原来的value
return old;
}
}

modCount++;

//判断是否需要扩容(再散列)
if (count >= threshold) {
rehash();

tab = table;
hash = hash(key);
index = (hash & 0x7FFFFFFF) % tab.length;
}

//如果没有对应的对应的key,新建一个Entry
Entry<K,V> e = tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;//即size++
return null;
}


          put方法中,首先检测value是否为null,如果为null则会抛出NullPointerException异常。然后往下走,跟HashMap的过程一样,先计算哈希值,再根据哈希值计算在数组中的索引位置,不过这里计算索引位置的方法和HashMap不同,HashMap里使用的是 hash & (length-1)的方法,其实本质上跟这里用的(hash & 0x7FFFFFFF) % table.length一样的效果,但是HashMap中的方法效率要高,至于它们两为啥本质一样的,可以参见我的上一博客:HashMap,那里分析的很详细。

           然后便开始往数组中存数据了,如果当前的key已经在里面了,那么直接替换原来旧的value,如果不存在,先判断数组中的Entry数量有没有达到门限值,达到了就要调用rehash方法进行扩容,然后重新计算当前key在新的数组中的索引值,然后在该位置添加进去即可。如下hash()rehash()方法:

 //jisuan计算hash值zhi
private int hash(Object k) {
return hashSeed ^ k.hashCode();
}

//再散列,扩容
protected void rehash() {
int oldCapacity = table.length;
Entry<K,V>[] oldMap = table;

//扩容为 = 原长度*2+1
int newCapacity = (oldCapacity << 1) + 1;
if (newCapacity - MAX_ARRAY_SIZE > 0) {
if (oldCapacity == MAX_ARRAY_SIZE)
return;
newCapacity = MAX_ARRAY_SIZE;
}
Entry<K,V>[] newMap = new Entry[newCapacity];

modCount++;
//重置阕值
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);

//重置hash种子
boolean rehash = initHashSeedAsNeeded(newCapacity);

table = newMap;

//将旧表的值放到新的表(数组)中
for (int i = oldCapacity ; i-- > 0 ;) {
for (Entry<K,V> old = oldMap[i] ; old != null ; ) {
Entry<K,V> e = old;
old = old.next;

if (rehash) {
e.hash = hash(e.key);
}
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = newMap[index];
newMap[index] = e;
}
}
}


到这里put方法就分析完了,还有个putAll方法,是将整个Map加到当前HashTable中,内部也是遍历每个Entry,然后调用上面的put方法:

putAll(Map<?
extends K,? extends V> t)

          将指定映射的所有映射关系复制到此哈希表中,这些映射关系将替换此哈希表拥有的、针对当前指定映射中所有键的所有映射关系。

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());
}




get(Object key) 

get(Object key)
          返回指定键所映射到的值,如果此映射不包含此键的映射,则返回 null. 更确切地讲,如果此映射包含满足 (key.equals(k)) 的从键 k 到值 v 的映射,则此方法返回 v;否则,返回null

public synchronized V get(Object key) {
Entry tab[] = table;
int hash = hash(key);//计算hash值
int index = (hash & 0x7FFFFFFF) % tab.length;//根据hash值计算所在索引
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;
}


其他方法:

    
//初始化hash种子
final boolean initHashSeedAsNeeded(int capacity) {
boolean currentAltHashing = hashSeed != 0;
boolean useAltHashing = sun.misc.VM.isBooted() &&
(capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
boolean switching = currentAltHashing ^ useAltHashing;
if (switching) {
hashSeed = useAltHashing
? sun.misc.Hashing.randomHashSeed(this)
: 0;
}
return switching;
}

//返回此哈希表中的键的数量。
public synchronized int size() {
return count;
}

//测试此哈希表是否没有键映射到值。
public synchronized boolean isEmpty() {
return count == 0;
}

//返回此哈希表中的键的枚举。
public synchronized Enumeration<K> keys() {
return this.<K>getEnumeration(KEYS);
}

private <T> Enumeration<T> getEnumeration(int type) {
if (count == 0) {//返回一个空的枚举类型
return Collections.emptyEnumeration();
} else {
return new Enumerator<>(type, false);
}
}

//返回此哈希表中的值的枚举。
public synchronized Enumeration<V> elements() {
return this.<V>getEnumeration(VALUES);
}

//测试此映射表中是否存在与指定值关联的键。
public synchronized boolean contains(Object value) {
if (value == null) {
throw new NullPointerException();
}

Entry tab[] = table;
for (int i = tab.length ; i-- > 0 ;) {
for (Entry<K,V> e = tab[i] ; e != null ; e = e.next) {
if (e.value.equals(value)) {
return true;
}
}
}
return false;
}

//如果此 Hashtable 将一个或多个键映射到此值,则返回 true。
public boolean containsValue(Object value) {
return contains(value);
}

//测试指定对象是否为此哈希表中的键。
public synchronized boolean containsKey(Object key) {
Entry tab[] = table;
int hash = hash(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)) {
return true;
}
}
return false;
}

//从哈希表中移除该键及其相应的值。
public synchronized V remove(Object key) {
Entry tab[] = table;
int hash = hash(key);
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.key.equals(key)) {
modCount++;
if (prev != null) {//如果该值不是表头
prev.next = e.next;
} else {//如果该值是表头,表头并不代表index=0
tab[index] = e.next;//换表头
}
count--;
V oldValue = e.value;
e.value = null;
return oldValue;
}
}
return null;
}

//将此哈希表清空,使其不包含任何键。
public synchronized void clear() {
Entry tab[] = table;
modCount++;
//按照table数组顺序分别将值至空
for (int index = tab.length; --index >= 0; )
tab[index] = null;
count = 0;
}

//创建此哈希表的浅表副本。
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) {
// this shouldn't happen, since we are Cloneable
throw new InternalError();
}
}

//返回此 Hashtable 对象的字符串表示形式,其形式为 ASCII 字符 ", " (逗号加空格)分隔开的、括在括号中的一组条目。
public synchronized String toString() {
int max = size() - 1;
if (max == -1)
return "{}";

StringBuilder sb = new StringBuilder();
//返回此映射中包含的键的 Set 视图的iterator迭代器。
Iterator<Map.Entry<K,V>> it = entrySet().iterator();

//将HashTable的key和value用StringBuilder拼接起来
sb.append('{');
for (int i = 0; ; i++) {
Map.Entry<K,V> e = it.next();
K key = e.getKey();
V value = e.getValue();
sb.append(key == this ? "(this Map)" : key.toString());
sb.append('=');
sb.append(value == this ? "(this Map)" : value.toString());

if (i == max)
return sb.append('}').toString();
sb.append(", ");
}
}

private <T> Iterator<T> getIterator(int type) {
if (count == 0) {//得到迭代器
return Collections.emptyIterator();
} else {//得到枚举
return new Enumerator<>(type, true);
}
}



/*-------------视图------------------*/
private transient volatile Set<K> keySet = null;
private transient volatile Set<Map.Entry<K,V>> entrySet = null;
private transient volatile Collection<V> values = null;
private static final int KEYS = 0;
private static final int VALUES = 1;
private static final int ENTRIES = 2;

/*++++++++++++++++键的 Set 视图+++++++++++++++++++*/
public Set<K> keySet() {
if (keySet == null)//指定 set 支持的同步(线程安全的)set。
keySet = Collections.synchronizedSet(new KeySet(), this);
return keySet;
}

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();
}
}

/*++++++++++++++++映射关系Entry的 set 视图+++++++++++++++++++*/
public Set<Map.Entry<K,V>> entrySet() {
if (entrySet==null)//指定 set 支持的同步(线程安全的)set。
entrySet = Collections.synchronizedSet(new EntrySet(), this);
return entrySet;
}

private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public Iterator<Map.Entry<K,V>> iterator() {
return getIterator(ENTRIES);//得到映射关系Entry的枚举或者空迭代器
}

public boolean add(Map.Entry<K,V> o) {
return super.add(o);
}

public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry entry = (Map.Entry)o;
Object key = entry.getKey();
Entry[] tab = table;
int hash = hash(key);
int index = (hash & 0x7FFFFFFF) % tab.length;

for (Entry e = tab[index]; e != null; e = e.next)
if (e.hash==hash && e.equals(entry))
return true;
return false;
}

public boolean remove(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<K,V> entry = (Map.Entry<K,V>) o;
K key = entry.getKey();
Entry[] tab = table;
int hash = hash(key);
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//如果该值是表头(表头并不代表index=0)
tab[index] = e.next;

count--;
e.value = null;
return true;
}
}
return false;
}

public int size() {
return count;
}

public void clear() {
Hashtable.this.clear();
}
}

/*++++++++++++++++value值的 set 视图+++++++++++++++++++*/
public Collection<V> values() {
if (values==null)//指定 collection 支持的同步(线程安全的)collection
values = Collections.synchronizedCollection(new ValueCollection(),
this);
return values;
}

private class ValueCollection extends AbstractCollection<V> {
public Iterator<V> iterator() {
return getIterator(VALUES);//得到value值的枚举或者空迭代器
}
public int size() {
return count;
}
public boolean contains(Object o) {
return containsValue(o);
}
public void clear() {
Hashtable.this.clear();
}
}



/*------------ 比较与hash------------------*/

//按照 Map 接口的定义,比较指定 Object 与此 Map 是否相等。
public synchronized boolean equals(Object o) {
if (o == this)
return true;

if (!(o instanceof Map))
return false;
Map<K,V> t = (Map<K,V>) o;
if (t.size() != size())
return false;

try {
Iterator<Map.Entry<K,V>> i = entrySet().iterator();
while (i.hasNext()) {
Map.Entry<K,V> e = i.next();
K key = e.getKey();
V value = e.getValue();
if (value == null) {
if (!(t.get(key)==null && t.containsKey(key)))
return false;
} else {
if (!value.equals(t.get(key)))
return false;
}
}
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}

return true;
}

//按照 Map 接口的定义,返回此 Map 的哈希码值。
public synchronized int hashCode() {

int h = 0;
//若HashTable的实际大小为0或者加载因子<0,则返回0
if (count == 0 || loadFactor < 0)
return h; // Returns zero

loadFactor = -loadFactor; // 使hashCode在程序中可计算(此处不懂为什么这样做)
Entry[] tab = table;

//返回“HashTable中的每个Entry的key和value的异或值的总和”
for (Entry<K,V> entry : tab)
while (entry != null) {
h += entry.hashCode();
entry = entry.next;
}
loadFactor = -loadFactor; // 使hashCode在程序中计算完成

return h;
}


// java.io.Serializable的写入函数
// 将Hashtable的“总的容量,实际容量,所有的Entry”都写入到输出流中
private void writeObject(java.io.ObjectOutputStream s)
throws IOException {
Entry<K, V> entryStack = null;

synchronized (this) {
// Write out the length, threshold, loadfactor
s.defaultWriteObject();

// Write out length, count of elements
s.writeInt(table.length);
s.writeInt(count);

// Stack copies of the entries in the table
for (int index = 0; index < table.length; index++) {
Entry<K,V> entry = table[index];

while (entry != null) {
entryStack =
new Entry<>(0, entry.key, entry.value, entryStack);
entry = entry.next;
}
}
}

// Write out the key/value objects from the stacked entries
while (entryStack != null) {
s.writeObject(entryStack.key);
s.writeObject(entryStack.value);
entryStack = entryStack.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<K,V>[] newTable = new Entry[length];
threshold = (int) Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1);
count = 0;
initHashSeedAsNeeded(length);

// 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(newTable, key, value);
}
this.table = newTable;
}


private void reconstitutionPut(Entry<K,V>[] 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 = hash(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)) {
throw new java.io.StreamCorruptedException();
}
}
// Creates the new entry.
Entry<K,V> e = tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
}

//枚举型迭代器(主要是keys() ,elements()两个函数调用)
//非线程安全的(除了该类的remove())
private class Enumerator<T> implements Enumeration<T>, Iterator<T> {
Entry[] table = Hashtable.this.table;
int index = table.length;
Entry<K,V> entry = null;
Entry<K,V> lastReturned = null;
int type;

boolean iterator;

protected int expectedModCount = modCount;

/*----------实现Enumeration接口的方法----------*/
Enumerator(int type, boolean iterator) {
this.type = type;
this.iterator = iterator;
}

public boolean hasMoreElements() {
Entry<K,V> e = entry;
int i = index;
Entry[] t = table;
/* 在本地实现迭代器,速度更快 */
//此处循环遍历是按照数组table索引从后向前遍历,找不为null的值
while (e == null && i > 0) {
e = t[--i];
}
entry = e;
index = i;
return e != null;
}

public T nextElement() {
Entry<K,V> et = entry;
int i = index;
Entry[] t = table;
/* 在本地实现迭代器,速度更快 */
//此处循环遍历是按照数组table索引从后向前遍历,找不为null的值
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接口的方法----------*/
public boolean hasNext() {
return hasMoreElements();
}

public T next() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
return nextElement();
}

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)//如果当前entry是表头
tab[index] = e.next;
else//如果当前entry不是表头
prev.next = e.next;
count--;
lastReturned = null;
return;
}
}
throw new ConcurrentModificationException();
}
}
}





Hashtable 的迭代


hashtable遍历有两种方式:

1、返回以哈希表的枚举方式;

①keys()

        返回此哈希表中的键的枚举。

②elements()

           返回此哈希表中的值的枚举。


2、返回以哈希表的Set方式;

keySet()

          返回此映射中包含的键的 Set 视图。

②values()

          返回此映射中包含的键的 Collection 视图。

③entrySet()

          返回此映射中包含的键的 Set 视图。


详细看代码:


package wn.comeOn.java.test;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;

public class Test{
public static void main(String[] args) {

//初始化Hashtable
Hashtable<Integer , Integer> tab = init();

//迭代以哈希表的枚举方式;
System.out.println("-------------枚举方式:-------------");
keysHashtable(tab);
elementsHashtable(tab);


//迭代以哈希表的Set方式;
System.out.println("-------------Set方式:-------------");
keySetHashtable(tab);
valuesHashtable(tab);
entrySetHashtable(tab);

}

//初始化
private static Hashtable<Integer, Integer> init() {

Hashtable<Integer, Integer> table = new Hashtable<>();
for(int i=0; i<1000000; i++){
table.put(i, i);
}
return table;

}

//枚举:keys()遍历key
private static void keysHashtable(Hashtable<Integer, Integer> tab) {

long start = System.currentTimeMillis();

Enumeration<Integer> iterator = tab.keys();
while(iterator.hasMoreElements()){
iterator.nextElement();
}

long end = System.currentTimeMillis();

System.out.println("枚举:keys()遍历key:" + (end-start));
}

//枚举:elements()遍历value
private static void elementsHashtable(Hashtable<Integer, Integer> tab) {
long start = System.currentTimeMillis();

Enumeration<Integer> iterator = tab.elements();
while(iterator.hasMoreElements()){
iterator.nextElement();
}

long end = System.currentTimeMillis();

System.out.println("枚举:elements()遍历value:" + (end-start));

}

//Set:keySet()遍历key
private static void keySetHashtable(Hashtable<Integer, Integer> tab) {
long start = System.currentTimeMillis();

Iterator iterator = tab.keySet().iterator();
while(iterator.hasNext()){
iterator.next();
}

long end = System.currentTimeMillis();

System.out.println("Set:keySet()遍历key:" + (end-start));

}

//Collection:values()遍历value
private static void valuesHashtable(Hashtable<Integer, Integer> tab) {
long start = System.currentTimeMillis();

Iterator iterator = tab.values().iterator();
while(iterator.hasNext()){
iterator.next();
}

long end = System.currentTimeMillis();

System.out.println("Collection:values()遍历value:" + (end-start));

}

//Set:entrySet()遍历entry
private static void entrySetHashtable(Hashtable<Integer, Integer> tab) {
long start = System.currentTimeMillis();

Iterator iterator = tab.entrySet().iterator();
while(iterator.hasNext()){
iterator.next();
}

long end = System.currentTimeMillis();

System.out.println("Set:entrySet()遍历entry:" + (end-start));

}

}
输出结果:

-------------枚举方式:-------------
枚举:keys()遍历key:23
枚举:elements()遍历value:13
-------------Set方式:-------------
Set:keySet()遍历key:15
Collection:values()遍历value:14
Set:entrySet()遍历entry:15

由代码可知,总体是这样:

枚举keys()   慢于 SetkeySet();

枚举的elements()  与 Collection的values() 遍历快慢取决于数据量


这是几组数据测试,代码还是上面的代码,只是更改了数据总量:

java源码分析之集合框架HashTable 11