JAVA深入集合--HashTable
public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable
? ?
?
? ? 1.继承了Dictionary,实现?Map接口,和?Cloneable,这里简单说一下。
? ??Dictionary:JDK 1.6阐明是一个过时的,用于存键值对的抽象父类,定义了一些基本属性,有兴趣自己看API。后来建议去实现Map 接口,而不是扩展此类,估计是面向接口编程,怕太臃肿了。只有hashtable继承,不是到有啥用,早就该去掉了。
?
? ??Map :作为代替Dictionary 的出现,也是定义了键值对的一些方法,好处是便于各种键值对的类自己去实现,让代码更轻了。
? ?Cloneable: 是克隆需要,Vactor 已经说了,就不多讲了。
?
? ?2.主要构造方法:
? ? ?
// 我们获得的hashtable对象,无论有参数,无参数的 最终都调用的这个构造。// 参数说明:// initialCapacity : 初始容量,好比新建数组,最开始的长度多少,默认11// loadFactor:增长因子,默认是0.75f,好比是 你的内容超过了 总长度了75%,就会自动扩容public Hashtable(int initialCapacity, float loadFactor) { // 初始长度小于0 必然异常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 实际上是 private transient Entry[] table; // Entry 是一个内部键值对的内部类,下面说 // 可以看到,hashTabale 实际上就是一个 Entry[]的数组table = new Entry[initialCapacity]; // 这里就是扩容的位置了,也就是会说你的数组容量超过了 下面这个数,默认就会扩容了threshold = (int)(initialCapacity * loadFactor); }
??
? ? 3.内部类Entry 解析:
? ??
private static class Entry<K,V> implements Map.Entry<K,V> {int hash;K key;V value; // 链表的动作,Map 实际有键值对和链表组成,具体的将hash算法再讨论Entry<K,V> next;
?
?
? 这里可以看到,这里是实现了Map.Entry 接口,定义了一些泛型属性,我们先看这个接口:
// Map 里面的内部接口interface Entry<K,V> {K getKey();V getValue();V setValue(V value);boolean equals(Object o);int hashCode(); }}
?
??
// HashTable 内部类的主要方法。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;}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());} }
?
? ? ?这里可能有些疑惑,既然实现了Map 接口,里面的getKey,getValue 等方法,为什么要用内部类去实现Map 的Entry接口,从而进行实现呢?不是很麻烦吗?
? ? ?这里我的理解是:提供了一个包含键值对的映射集,这东西更加方便我们进行迭代(遍历),提供一种统一的迭代方式(Iterator),我们获得这个映射之后,可以同时获得 单个的键和值,并且迭代期间任何非自身的操作,都会报错,保证这个映射的唯一性,当然只能通过内部setValue(),进行设置值。
? ? ? 同时这个键值对在其他地方 有很灵活的使用,让你获得键 值 等个各种方式,更加灵活,后面会有
? ? ??
? ? ? 其实HashTbale 自身提供了 获得key 和value 的方式,请看源码:
? ? ??
// 获得valuesprivate transient volatile Collection<V> values = null;?
?
? ?
// 这是获得所有值的方式public Collection<V> values() {if (values==null) // 这里是通过一个内部,和 当前hashMap 对象,通过collection 方法获得值的集合 // 这里使用的是线程安全的方法 values = Collections.synchronizedCollection(new ValueCollection(), this); return values; } 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(); } }?
?
? ? 顺便看看Collections.synchronizedCollection 源码:
??
static <T> Collection<T> synchronizedCollection(Collection<T> c, Object mutex) {return new SynchronizedCollection<T>(c, mutex); }final Collection<E> c; // Backing Collectionfinal Object mutex; // Object on which to synchronize// 这里就只有一个赋值操作SynchronizedCollection(Collection<E> c, Object mutex) { this.c = c; this.mutex = mutex; }?
?
? ?从代码上看,相当于是把?ValueCollection 这个内部类 和hashtable 传入进去赋值给对象了。
? ?1.ValueCollection 里面有iterator(),size() 等方法。这里相当于colleaction 方法里面有几乎所有
? ? ?集合的基本方法接口,然后实现在各个集合里面。但是集合里面定义内部类 同样调用实现那些实现方 ? ? ?法的作用是为了将内部类 传回给colleaction ,让集合具有一种通用的访问这些元素的方法。
? ?比如:Collection 定义集合可以看电影。hashtable 实现 用电脑看电影。 然后定义内部类,里面把用电脑看电影这种方式也放进去,然后可以送给Collection 集合,那么它就可以知道是用电脑看电影了。 也就是说,看电影这种举动很多集合都有,Collection 作为一种工具,只要任何其他集合传入一个方式过来,就可以调用各自的看电影的方式了。
? ? 2.参数this,实际上是一把对锁,实现线程安全的。这里看Collection 里面对这个内部类的操作就知道
?
? ??
// 这是获得,key 的集合private transient volatile Set<K> keySet = null;// 上面的key,valuesprivate transient volatile Set<Map.Entry<K,V>> entrySet = null;??
public Set<K> keySet() {if (keySet == null) 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(); } }?
?
? 这里你会发现,获得keys 和values方法几乎一样,只是这里多提供了一个remove 方法,
? 那么这两个相同的方法 ?是如何 分别获得key 和value 的呢?请看:
??
// 这是主要不同点:
public Iterator<K> iterator() { return getIterator(KEYS); }public Iterator<K> iterator() { return getIterator(VALUES); } // 这几个参数 对应了 ,返回的类型,表示 枚举 private static final int KEYS = 0; private static final int VALUES = 1; private static final int ENTRIES = 2;
?
? ?
// 方法,发现这里其实是返回的是一个内部类, count = 0 的也是内部类,这里不介绍了 ,里面就判// 定空值的,主要看Enumerator private <T> Iterator<T> getIterator(int type) {if (count == 0) { return (Iterator<T>) emptyIterator;} else { return new Enumerator<T>(type, true);}??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;........// 构造Enumerator(int type, boolean iterator) { this.type = type; this.iterator = iterator;}? ?// 在 获得下一个元素的时候 就能使用了public T nextElement() { Entry<K,V> et = entry; int i = index; // 将当前数组 给这个变量 Entry[] t = table; /* Use locals for faster loop iteration */ // 循环遍历 数组,并将数组里面的Entry<K,V> 类型的元素赋值 while (et == null && i > 0) {et = t[--i]; } entry = et; index = i; if (et != null) {Entry<K,V> e = lastReturned = entry; // 获得为Entry<K,V> 类型的键值对entry = e.next; // 这里就可以返回 键 值 还是键值对了。这就明白当初定义内部类 Entry好处return type == KEYS ? (T)e.key : (type == VALUES ? (T)e.value : (T)e); } throw new NoSuchElementException("Hashtable Enumerator");}??public Set<K> keySet() {if (keySet == null) keySet = Collections.synchronizedSet(new KeySet(), this);return keySet; }? 包括获得枚举类型key 的方式: public synchronized Enumeration<K> keys() {return this.<K>getEnumeration(KEYS); } public synchronized V put(K key, V value) {// Make sure the value is not null // 这里说明了值不能为空的if (value == null) { throw new NullPointerException();}// Makes sure the key is not already in the hashtable.Entry tab[] = table; // 下面实际上通过 一些算法计算存放位置,具体hash 等算法的研究,以后专门讲解int hash = key.hashCode();int index = (hash & 0x7FFFFFFF) % tab.length;for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) { // 这里通过hash 和 值的比较,如果重复就覆盖 if ((e.hash == hash) && e.key.equals(key)) {V old = e.value;e.value = value;return old; }} modCount++; // 超过临界值,从新 计算if (count >= threshold) { // Rehash the table if the threshold is exceeded rehash(); tab = table; index = (hash & 0x7FFFFFFF) % tab.length;} // 存放值// Creates the new entry.Entry<K,V> e = tab[index];tab[index] = new Entry<K,V>(hash, key, value, e);count++;return null; }? public synchronized V get(Object key) {Entry tab[] = table;int hash = key.hashCode(); // 和上面差不多,计算位置的 // PS:JDK 这里写得重复了, 代码还是可以提炼的嘛 呵呵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; }?public synchronized boolean contains(Object value) {if (value == null) { throw new NullPointerException();}Entry tab[] = table; // 说白了就是一个Entry 内部元素遍历。简单的说 就是数组遍历啦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; }?public synchronized boolean containsKey(Object key) {Entry tab[] = table;int hash = key.hashCode();int index = (hash & 0x7FFFFFFF) % tab.length; // 其实还是Entry 元素访问,数组遍历,当然这里 对key 进行了hash 比较,有些位置重复嘛 // PS:看来重复代码比较多,hashmap 估计会好很多for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) {return true; }}return false; }?? 差不多 就介绍完了,其他方法,都类似的。? 总结:? 1.hashtable 是一种键值对存放的数据结构? 2.它是有一个内部类 Entry 的的形式存放,将所有元素以Entry<K,V> 方式存放在数组table里面。
? ? ??