java.util.Hashtable源码解析
来源:互联网 发布:怎么查看手机mac地址 编辑:程序博客网 时间:2024/04/30 01:42
1.java集合框架图
2.所属包
package java.util;
3.继承与实现关系
public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, java.io.Serializable
4.属性
/** * 存放Entry的数组 */ private transient Entry<K,V>[] table; /** * 哈希表中Entry的数量 */ private transient int count; /** * 哈希表的临界值 threshold =capacity*loadFactor */ private int threshold; /** * 哈希表的加载因子 */ private float loadFactor; /** * 被修改的次数 */ private transient int modCount = 0; /** * 默认的hash临界值 */ static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;
5.Hashtable的数据结构
哈希表的核心就是根据元素求位置。而多个元素可能出现相同的位置,那么就叫冲突。解决冲突的常用方式:链地址法:将多个值的不同的哈希结果(哈希值)用数组进行存储,然后将产生相同哈希值的元素,以单向链表的形式进行存储。所以拉链法的套路就是数组+单链表。第二种解决方式是线性探测法:将多个值余上表长度,如果多个值产生相同的哈希值,那么就依次往下寻找位置,直到不冲突为止。线性探测法致命的缺点就是当数据量上来时,就会频繁的进行碰撞冲突,然后找到位置比较费劲。
/** * 采用单向链表的方式进行连接 */ private static class Entry<K,V> implements Map.Entry<K,V> { int hash; final K key; V value; Entry<K,V> next; protected Entry(int hash, K key, V value, Entry<K,V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } 。。。。。。 }
6.构造方法
/** 构造方法1 * 指定初始化容量和加载因子参数的构造方法 */ public Hashtable(int initialCapacity, float loadFactor) {//初始化容量小于0,则抛出非法参数异常 if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);//加载因子小于0或者加载因子非数字,则抛出非法参数异常 if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal Load: "+loadFactor);//如果初始化容量为0,那么就初始化为1 if (initialCapacity==0) initialCapacity = 1; this.loadFactor = loadFactor; table = new Entry[initialCapacity]; threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1); useAltHashing = sun.misc.VM.isBooted() && (initialCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD); }
/** 构造方法2 * 指定初始化容量,采用默认加载因子0.75的构造方法 */ public Hashtable(int initialCapacity) { this(initialCapacity, 0.75f); }
/** 构造方法3 * 构造一个初始化容量为11,加载因子为0.75的构造方法 */ public Hashtable() { this(11, 0.75f); }
/** 构造方法4 * 通过给定的Map来构造HashTable. * * @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) { this(Math.max(2*t.size(), 11), 0.75f); putAll(t); }
7.方法
hash方法:
//通过键来获取hash值 private int hash(Object k) { if (useAltHashing) { if (k.getClass() == String.class) { return sun.misc.Hashing.stringHash32((String) k); } else { int h = hashSeed ^ k.hashCode(); // This function ensures that hashCodes that differ only by // constant multiples at each bit position have a bounded // number of collisions (approximately 8 at default load factor). h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } } else { return k.hashCode(); } }putAll方法:
/** * 采用同步的方式插入值 */ 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方法:
/** * 将指定 key 映射到此哈希表中的指定 value。键和值都不可以为 null。 */ public synchronized V put(K key, V value) { // 如果插入的值为null,抛出空指针异常 if (value == null) { throw new NullPointerException(); } Entry tab[] = table; int hash = hash(key);//获取下标索引 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)) { V old = e.value; e.value = value; return old; } } modCount++;//如果表长超出了临界值,重新进行hash值计算,并重新设置Entry数组、hash值、下标索引 if (count >= threshold) { rehash(); tab = table; hash = hash(key); index = (hash & 0x7FFFFFFF) % tab.length; } // 创建新的Entry Entry<K,V> e = tab[index]; tab[index] = new Entry<>(hash, key, value, e); count++; return null; }rehash方法:
/** * 增加此哈希表的容量并在内部对其进行重组,以便更有效地容纳和访问其元素。当哈希表中的键的数量超出哈希表的容量和加载因子时,自动调用此方法。 */ protected void rehash() { int oldCapacity = table.length; Entry<K,V>[] oldMap = table; // 新容量为旧容量的二倍加1 int newCapacity = (oldCapacity << 1) + 1;//如果新容量比数组的最大值还大,就将数组的最大值作为新容量 if (newCapacity - MAX_ARRAY_SIZE > 0) { 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); boolean currentAltHashing = useAltHashing; useAltHashing = sun.misc.VM.isBooted() && (newCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD); boolean rehash = currentAltHashing ^ useAltHashing; table = newMap;//遍历旧表中的元素,将旧表中的元素都转移到新表中 for (int i = oldCapacity ; i-- > 0 ;) {//遍历拥有相同hash值的单向链表 for (Entry<K,V> old = oldMap[i] ; old != null ; ) { Entry<K,V> e = old; old = old.next;//重新通过键生成hash值 if (rehash) { e.hash = hash(e.key); }//通过取模的方式来求下标索引 int index = (e.hash & 0x7FFFFFFF) % newCapacity; e.next = newMap[index]; newMap[index] = e; } } }contains方法:
/** * 以同步的方式判断hash表中是否包含指定的值 */ public synchronized boolean contains(Object value) {//如果值为空,则抛出空指针异常 if (value == null) { throw new NullPointerException(); } Entry tab[] = table;//遍历每一条单链表中的值,如果存在与输入的值相同,那么返回true 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; }containsValue方法:
/** * 以同步的方式判断hash表中是否包含指定的值 */ public boolean containsValue(Object value) { return contains(value); }containsKey方法:
/** * 以同步的方式来判断hash表中是否存在输入的键 */ public synchronized boolean containsKey(Object key) { Entry tab[] = table;//通过键获取对应的hash值 int hash = hash(key);//通过hash值获取对应的下标索引 int index = (hash & 0x7FFFFFFF) % tab.length;//根据索引获取的Entry遍历单链表,如果存在hash值和键都相同的,那么就返回true。 for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { return true; } } return false; }get方法:
/** * 使用同步的方式通过键获取值 */ public synchronized V get(Object key) { Entry tab[] = table;//通过键获取hash值 int hash = hash(key);//通过取模操作来获取下标索引 int index = (hash & 0x7FFFFFFF) % tab.length;//遍历该索引对应的那条单链表,如果存在hash值和键都相同的Entry,就返回该Entry对应的值。 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; }remove方法:
/** * 通过键获取hash值 * 通过hash进行取模获取下标索引 * 通过索引获取Entry遍历单链表,找到hash和key都相同的元素将其删除,并调整其前后关系。 * 返回删除的旧值 */ public synchronized V remove(Object key) { Entry tab[] = table;//使用键获取对应的hash值 int hash = hash(key);//使用取模操作来获取下标索引 int index = (hash & 0x7FFFFFFF) % tab.length;//遍历该索引对应的单链表,遍历找到hash值和键都相同的点,删除之后需要调整单链表中节点的前后关系。 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++;/* prev变量赋值为e * 如果prev不为空,那么e的下一个节点为prev的下一个节点。 * 如果prev为空,那么e的下一个节点为作为单链表的第一个节点(也对应着Entry数组中的值)*/ if (prev != null) { prev.next = e.next; } else { tab[index] = e.next; }//删除一个值,长度减一 count--; V oldValue = e.value;//将删除的节点e的值设置为空,没有了引用,这样便于编译器回收 e.value = null; return oldValue; } } return null; }clear方法:
/** * 遍历表table,将元素置为空,长度设置为0 */ public synchronized void clear() { Entry tab[] = table; modCount++; for (int index = tab.length; --index >= 0; ) tab[index] = null; count = 0; }
-----------------------------该源码为jdk1.7版本的
阅读全文
0 0
- java.util.Hashtable源码解析
- 【jdk源码解析三】java.util.Hashtable
- java HashTable源码解析
- Java集合Hashtable源码解析
- java.util.logging源码解析
- java.util.ArrayList源码解析
- java.util.LinkedList源码解析
- java.util.Vector源码解析
- java.util.Stack源码解析
- java.util.HashSet源码解析
- java.util.HashMap源码解析
- java.util.LinkedHashMap源码解析
- java.util.ArrayDeque源码解析
- java.util.TreeMap源码解析
- java.util.ArrayList源码解析
- JAVA 集合类(java.util)源码阅读笔记------Hashtable
- java.util.Hashtable翻译
- java.util.Hashtable翻译
- @Param 注解在Mybatis中的使用 以及传递参数的三种方式
- FAFU OJ 一个简单的问题
- pmon读取一个64位寄存器值死机
- 链表和顺序表习题(一)
- 最详细的Log4j使用教程
- java.util.Hashtable源码解析
- IPC-消息队列
- View组件
- 欢迎使用CSDN-markdown编辑器
- NSIS如何编写带参数的函数
- jQuery Mobile Data 属性
- HDU 1166
- nginx返回json或者文本格式详解
- java并发编程