在上一篇学习过HashMap(源码解读—HashMap)之后对hashTable也产生了兴趣,随即便把hashTable的源码看了一下。和hashMap类似,但是也有不同之处。

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

实现接口:Map,Cloneable,Serializable

继承自Dictionary,此抽象类同AbstractMap,

1、四个构造方法:

第一个构造方法: public Hashtable() ;

    /**
* 用默认容量(11)和加载系数(0.75)构造一个空的hashTable
* Constructs a new, empty hashtable with a default initial capacity (11)
* and load factor (0.75).
*/
public Hashtable() {
//此处竟然没有定义成常量!!!看来oracle的开发人员也是人!!!
this(11, 0.75f);
}

第二个构造方法:  public Hashtable(int initialCapacity)

    /**
* 用指定的容量和默认的系数构造一个新的hashTable
* Constructs a new, empty hashtable with the specified initial capacity
* and default load factor (0.75).
*
* @param initialCapacity the initial capacity of the hashtable.
* @exception IllegalArgumentException if the initial capacity is less
* than zero.
*/
public Hashtable(int initialCapacity) {
this(initialCapacity, 0.75f);
}

第三个构造方法: public Hashtable(int initialCapacity, float loadFactor);

    /**
* 用指定的容量和加载系数构造一个空的hashTable
* Constructs a new, empty hashtable with the specified initial
* capacity and the specified load factor.
*
* @param initialCapacity the initial capacity of the hashtable.
* @param loadFactor the load factor of the hashtable.
* @exception IllegalArgumentException if the initial capacity is less
* than zero, or if the load factor is nonpositive.
*/
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);
//初始化容量不允许为0,最小为1
if (initialCapacity==0)
initialCapacity = 1;
this.loadFactor = loadFactor;
//数组+单向链表作为hashTable的数据存储容器。初始化数组
table = new Entry[initialCapacity];
//计算扩容阀值,当count(hashTable中的所有entry,不是table数组的size) >= threshold的时候进行扩容操作。
//扩容阀值=容量*加载系数 扩容法制最大不超过(MAX_ARRAY_SIZE + 1)
threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
//初始化hashSeed,在进行hashCode计算的时候需要使用
initHashSeedAsNeeded(initialCapacity);
}

第四个构造方法:public Hashtable(Map<? extends K, ? extends V> t)

    /**
* Constructs a new hashtable with the same mappings as the given
* Map. The hashtable is created with an initial capacity sufficient to
* hold the mappings in the given Map and a default load factor (0.75).
*
* @param t the map whose mappings are to be placed in this map.
* @throws NullPointerException if the specified map is null.
* @since 1.2
*/
public Hashtable(Map<? extends K, ? extends V> t) {
//TODO 创建一个新的hashTable,这里可以肯出hashTable的加载系数一直都是0.75,不允许调用处进行设置
//当t的count>11的时候程序取2*t.size(),为甚么取2倍? 标示:XX01
this(Math.max(2*t.size(), 11), 0.75f);
//把数据转存到hashTable中
putAll(t);
}

在这里调用  public synchronized void putAll(Map<? extends K, ? extends V> t) ;把所有数据放进hashTable中

    /**
* 把所有的 映射从指定的map复制到hashTable中
* 如果给定的map中的key值已经存在于hashTable中,则将会覆盖hashTable中key所对应的value(hashTable中key值不允许重复)
* Copies all of the mappings from the specified map to this hashtable.
* These mappings will replace any mappings that this hashtable had for any
* of the keys currently in the specified map.
*
* @param t mappings to be stored in this map
* @throws NullPointerException if the specified map is null
* @since 1.2
*/
public synchronized void putAll(Map<? extends K, ? extends V> t) {
//foreach 循环map数据put到hashTable中
for (Map.Entry<? extends K, ? extends V> e : t.entrySet())
put(e.getKey(), e.getValue());
}

循环调用  public synchronized V put(K key, V value) ;进行entry复制

   /**
* 把给定的key和value进行映射后放入hashTable,
* key和value值都不允许为null(hashMap是允许key和value为null的)
* Maps the specified <code>key</code> to the specified
* <code>value</code> in this hashtable. Neither the key nor the
* value can be <code>null</code>. <p>
*
*通过调用get(K key)方法可以取出value值
* The value can be retrieved by calling the <code>get</code> method
* with a key that is equal to the original key.
*
* @param key the hashtable key
* @param value the value
* @return the previous value of the specified key in this hashtable,
* or <code>null</code> if it did not have one
* @exception NullPointerException if the key or value is
* <code>null</code>
* @see Object#equals(Object)
* @see #get(Object)
*/
//synchronized:看到这个关键字没?这就是hashTable线程安全的原因,加锁了,不允许两个线程同时操作此方法
public synchronized V put(K key, V value) {
// Make sure the value is not null 不允许value为null,否则抛出 空指针异常
if (value == null) {
throw new NullPointerException();
} // Makes sure the key is not already in the hashtable.
Entry tab[] = table;
//取hashCode
int hash = hash(key);
//计算table下标(hashTable的数据存储容器为数组+链表(链表是为了解决hash冲突))
//至于为什么这样计算?我也不知道,智商是硬伤啊!
int index = (hash & 0x7FFFFFFF) % tab.length;
//从table取出entrust(链表头结点,如果有的话),然后循环链表找到key,如果找到则进行value覆盖做操
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;
}
}
//如果hashTable没有该映射则新增
//操作次数++
modCount++;
//判断是否需要进行扩容,注意:threshold不是table的长度,而是capacity*loadFactor
if (count >= threshold) {
// Rehash the table if the threshold is exceeded 如果阀值过大则进行扩容
rehash();
tab = table;
hash = hash(key);
index = (hash & 0x7FFFFFFF) % tab.length;
} // Creates the new entry. 创建一个新的节点,把他放入table
Entry<K,V> e = tab[index];
tab[index] = new Entry<>(hash, key, value, e);
//数据量++
count++;
return null;
}

2、其他重点方法

keySet和valueSet的迭代器使用

    /**
* 返回key的迭代器/枚举器?该怎么叫呢?
* Returns an enumeration of the keys in this hashtable.
*
* @return an enumeration of the keys in this hashtable.
* @see Enumeration
* @see #elements()
* @see #keySet()
* @see Map
*/
public synchronized Enumeration<K> keys() {
return this.<K>getEnumeration(KEYS);
} /**
* 返回value的枚举器,通过这个没去器可以连续取出value的值
* Returns an enumeration of the values in this hashtable.
* Use the Enumeration methods on the returned object to fetch the elements
* sequentially.
*
* @return an enumeration of the values in this hashtable.
* @see java.util.Enumeration
* @see #keys()
* @see #values()
* @see Map
*/
public synchronized Enumeration<V> elements() {
return this.<V>getEnumeration(VALUES);
} //根据type(KEY/VALUE)取出对应的枚举器
private <T> Enumeration<T> getEnumeration(int type) {
//如果hashTable数据为空则返回空的枚举器
if (count == 0) {
return Collections.emptyEnumeration();
} else {
return new Enumerator<>(type, false);
}
} //获取迭代器,没什么可分析的,如果不了解可以找一下迭代器的使用
private <T> Iterator<T> getIterator(int type) {
if (count == 0) {
return Collections.emptyIterator();
} else {
return new Enumerator<>(type, true);
}
}

根据key取出value: public synchronized V get(Object key);

    /**
* 取出给定的key所映射的value,如果hashTable中没有此key则返回null
* Returns the value to which the specified key is mapped,
* or {@code null} if this map contains no mapping for the key.
*
*下面又啰嗦了一遍,这里的key相等时根据key.equals(k)来进行判断的,也就是字符串内容相等就OK
* <p>More formally, if this map contains a mapping from a key
* {@code k} to a value {@code v} such that {@code (key.equals(k))},
* then this method returns {@code v}; otherwise it returns
* {@code null}. (There can be at most one such mapping.)
*
* @param key the key whose associated value is to be returned
* @return the value to which the specified key is mapped, or
* {@code null} if this map contains no mapping for the key
* @throws NullPointerException if the specified key is null
* @see #put(Object, Object)
*/
public synchronized V get(Object key) {
//还是套路:计算index,从table中取出entry节点,再循环链表找key.equals(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) {
//判断相等的时候需要key的hashCode和内容同时相同才可
if ((e.hash == hash) && e.key.equals(key)) {
return e.value;
}
}
return null;
}

扩容操作:当count(entry节点的数量/Key的数量) >= threshold 时进行扩容

   /**
* 增加容量并且对hashTable进行内部重整,以便于有效的接收容纳更多的entry。
* 当key的数量大于hashTable的阀值时会自动调用这个方法。
* Increases the capacity of and internally reorganizes this
* hashtable, in order to accommodate and access its entries more
* efficiently. This method is called automatically when the
* number of keys in the hashtable exceeds this hashtable's capacity
* and load factor.
*/
protected void rehash() {
//套路:先保存老数据
int oldCapacity = table.length;
Entry<K,V>[] oldMap = table; // overflow-conscious code
//计算出新的容量:(oldCapacity*2)+1
int newCapacity = (oldCapacity << 1) + 1;
//如果扩容之后的容量大于容量的最大值则进行判断
if (newCapacity - MAX_ARRAY_SIZE > 0) {
//如果容量已经是最大值了则无法集训进行扩容,只能return了。终止操作
if (oldCapacity == MAX_ARRAY_SIZE)
// Keep running with MAX_ARRAY_SIZE buckets
return;
//否则取最大值
newCapacity = MAX_ARRAY_SIZE;
} Entry<K,V>[] newMap = new Entry[newCapacity];
//操作此时++
modCount++;
//重新计算 阀值
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
//TODO 标示:XX03
boolean rehash = initHashSeedAsNeeded(newCapacity); table = newMap;
//循环table
for (int i = oldCapacity ; i-- > 0 ;) {
//循环链表
for (Entry<K,V> old = oldMap[i] ; old != null ; ) {
//注意这里,这里传递的知识引用,也就是说在堆空间中并没有复制一个新的entry,
//只是把原来的entry的引用复制了一份(不了解的可以查找一下java 的堆栈内存数据存储)
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];
//把老数据放到新的table中
newMap[index] = e;
}
}
}

hashTable继承了序列化和克隆接口,在这里没有对序列化读写方法和克隆方法进行分析,这些都是固定的格式,感兴趣的朋友可以看一些这些接口的使用方法。

在这里还涉及到keySet valueSet 和entrySet,这主要是用来进行迭代使用的,对迭代器不了解的可以找一些迭代器使用的相关资料,这些都是套路,不再分析。

所有的数据操作都离不开增删改查,而这些不同方法很多步骤都是相同的,先进行数据验证,然后进行数据查找,找到之后进行删、改操作。抓住核心其实就没什么难理解的了。

-------------------------------------over--------------------------------------------------

源码解读—HashTable的更多相关文章

  1. HashTable、HashMap与ConCurrentHashMap源码解读

    HashMap 的数据结构 ​ hashMap 初始的数据结构如下图所示,内部维护一个数组,然后数组上维护一个单链表,有个形象的比喻就是想挂钩一样,数组脚标一样的,一个一个的节点往下挂. ​ 我们可以 ...

  2. jdk1.8.0_45源码解读——HashMap的实现

    jdk1.8.0_45源码解读——HashMap的实现 一.HashMap概述 HashMap是基于哈希表的Map接口实现的,此实现提供所有可选的映射操作.存储的是<key,value>对 ...

  3. jdk1.8.0_45源码解读——Map接口和AbstractMap抽象类的实现

    jdk1.8.0_45源码解读——Map接口和AbstractMap抽象类的实现 一. Map架构 如上图:(01) Map 是映射接口,Map中存储的内容是键值对(key-value).(02) A ...

  4. SDWebImage源码解读之SDWebImageDownloaderOperation

    第七篇 前言 本篇文章主要讲解下载操作的相关知识,SDWebImageDownloaderOperation的主要任务是把一张图片从服务器下载到内存中.下载数据并不难,如何对下载这一系列的任务进行设计 ...

  5. SDWebImage源码解读 之 NSData+ImageContentType

    第一篇 前言 从今天开始,我将开启一段源码解读的旅途了.在这里先暂时不透露具体解读的源码到底是哪些?因为也可能随着解读的进行会更改计划.但能够肯定的是,这一系列之中肯定会有Swift版本的代码. 说说 ...

  6. SDWebImage源码解读 之 UIImage+GIF

    第二篇 前言 本篇是和GIF相关的一个UIImage的分类.主要提供了三个方法: + (UIImage *)sd_animatedGIFNamed:(NSString *)name ----- 根据名 ...

  7. SDWebImage源码解读 之 SDWebImageCompat

    第三篇 前言 本篇主要解读SDWebImage的配置文件.正如compat的定义,该配置文件主要是兼容Apple的其他设备.也许我们真实的开发平台只有一个,但考虑各个平台的兼容性,对于框架有着很重要的 ...

  8. SDWebImage源码解读_之SDWebImageDecoder

    第四篇 前言 首先,我们要弄明白一个问题? 为什么要对UIImage进行解码呢?难道不能直接使用吗? 其实不解码也是可以使用的,假如说我们通过imageNamed:来加载image,系统默认会在主线程 ...

  9. SDWebImage源码解读之SDWebImageCache(上)

    第五篇 前言 本篇主要讲解图片缓存类的知识,虽然只涉及了图片方面的缓存的设计,但思想同样适用于别的方面的设计.在架构上来说,缓存算是存储设计的一部分.我们把各种不同的存储内容按照功能进行切割后,图片缓 ...

随机推荐

  1. 一道模拟题:改进的Joseph环

    题目:改进的Joseph环.一圈人报数,报数上限依次为3,7,11,19,循环进行,直到所有人出列完毕. 思路:双向循环链表模拟. 代码: #include <cstdio> #inclu ...

  2. 服务器端与客户端TCP连接入门(三:多线程)

    对于服务器端来说,如果要加入多线程机制,则应该在每个用户连接之后启动一个新的线程 建立一个EchoThread类,此类专门用于处理多线程操作,此时的多线程使用Runnable接口实现 package ...

  3. Mutual Training for Wannafly Union #1解题报告

    ---恢复内容开始--- q神等人组织的vjudge上的多校训练,题目基本上都来自于CF,#1是上周进行的,参加后感觉收获很多,因为上周准备期中比较忙,解题报告现在补上. 比赛地址(兼题目地址) A题 ...

  4. enmo_day_05

    大文件表空间 小文件表空间 临时表空间 :不需自己删除,session断了之后自动删除 永久表空间 : 本地管理表空间 :使用位图表示表空间,0表示没有数据,1表示有数据, 数据字典管理表空间 eg ...

  5. C#面向对象总结2

    1.值类型和引用类型: 值类型:int.double.bool.char.decimal.struct.enum 引用类型:string.自定义类.数组 存储: 值类型的值是存储在内存的栈当中. 引用 ...

  6. javaEE(web)SEO优化 Yahoo军规

    javaEE(web)SEO优化 Yahoo军规 1.尽可能减少HTTP请求数2.使用CDN3.添加Expire/Cache-Control头4.启用Gzip压缩5.将CSS房在页面最上方6.将Scr ...

  7. animate.css总结

    本文对animate.css的各个效果进行总结 bounce 从上掉落,在地上小幅度跳起 <!DOCTYPE html> <meta charset="utf-8" ...

  8. nl2br() 函数

    nl2br() 函数在字符串中的每个新行 (\n) 之前插入 HTML 换行符 (<br />).经常用于在线编辑的内容,以便显示. 用法:nl2br(string) 参数 描述 stri ...

  9. Using python to process Big Data

    Pandas is a great lib to process BIg Data. 1) pandas.pivot_table(data,values=None,columns=None,aggfu ...

  10. zTree简单使用和代码结构

    1.页面使用元素代码 <input type="text" id="key" class="Side_Toput2" name=&qu ...