Hashtable
的基本介绍
- 存放的元素是键值对象:即
K-V
Hashtable
的键和值都不能存放null
,否则会报空指针异常Hashtable
使用方法基本上和HashMap
一样Hashtable
是线程安全的,HashMap
是线程不安全的- 扩容机制是以2倍再加1的方式进行扩容
int newCapacity = (oldCapacity << 1) + 1;
构造器
常用方法
Modifier and Type | Method and Description |
---|---|
void | clear() 清除此散列表,使其不包含键。 |
Object | clone() 创建这个散列表的浅拷贝。 |
V | compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) 尝试计算指定键的映射及其当前映射的值(如果没有当前映射, null )。 |
V | computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction) 如果指定的键尚未与某个值相关联(或映射到 null ),则尝试使用给定的映射函数计算其值,并将其输入到此映射中,除非 null 。 |
V | computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) 如果指定的密钥的值存在且非空,则尝试计算给定密钥及其当前映射值的新映射。 |
boolean | contains(Object value) 测试一些键映射到这个哈希表中的指定值。 |
boolean | containsKey(Object key) 测试指定的对象是否在此哈希表中的键。 |
boolean | containsValue(Object value) 如果此哈希表将一个或多个键映射到此值,则返回true。 |
Enumeration<V> | elements() 返回此散列表中值的枚举。 |
Set<Map.Entry<K,V>> | entrySet() 返回此地图中包含的映射的Set 视图。 |
boolean | equals(Object o) 根据Map界面中的定义,将指定的对象与此Map进行比较以相等。 |
void | forEach(BiConsumer<? super K,? super V> action) 对此映射中的每个条目执行给定的操作,直到所有条目都被处理或操作引发异常。 |
V | get(Object key) 返回到指定键所映射的值,或 null 如果此映射包含该键的映射。 |
V | getOrDefault(Object key, V defaultValue) 返回到指定键所映射的值,或 defaultValue 如果此映射包含该键的映射。 |
int | hashCode() 按照Map界面中的定义返回此Map的哈希码值。 |
boolean | isEmpty() 测试这个哈希表是否将值映射到值。 |
Enumeration<K> | keys() 返回此散列表中键的枚举。 |
Set<K> | keySet() 返回此地图中包含的键的Set 视图。 |
V | merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction) 如果指定的键尚未与值相关联或与null相关联,则将其与给定的非空值相关联。 |
V | put(K key, V value) 将指定的 key 映射到此 key value 中指定的value。 |
void | putAll(Map<? extends K,? extends V> t) 将所有从指定地图的映射复制到此散列表。 |
V | putIfAbsent(K key, V value) 如果指定的键尚未与值相关联(或映射到 null )将其与给定值相关联并返回 null ,否则返回当前值。 |
protected void | rehash() 增加这个散列表的内部重组能力,从而更有效地适应和访问其条目。 |
V | remove(Object key) 从此散列表中删除键(及其对应的值)。 |
boolean | remove(Object key, Object value) 仅当指定的密钥当前映射到指定的值时删除该条目。 |
V | replace(K key, V value) 只有当目标映射到某个值时,才能替换指定键的条目。 |
boolean | replace(K key, V oldValue, V newValue) 仅当当前映射到指定的值时,才能替换指定键的条目。 |
void | replaceAll(BiFunction<? super K,? super V,? extends V> function) 将每个条目的值替换为对该条目调用给定函数的结果,直到所有条目都被处理或该函数抛出异常。 |
int | size() 返回此哈希表中的键数。 |
String | toString() 以一组条目的形式返回此 Hashtable 对象的字符串表示形式,其括在大括号中,并以ASCII字符“ , ”(逗号和空格)分隔。 |
Collection<V> | values() 返回此地图中包含的值的Collection 视图。 |
Hashtable
底层源码剖析
在使用无参构造器创建对象时,会在底层创建一个大小为 11 的table = new Entry<?,?>[initialCapacity]; 创建一个 Entry<?,?>的数组为 11
数组
步骤1:使用无参构造器 创建一个Hashtable 对象
public Hashtable() {
this(11, 0.75f); 调用本类中的有参构造器
}
创建一个
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; 对加载因子赋值为 0.75
table = new Entry<?,?>[initialCapacity]; 创建一个 Entry<?,?>的数组为 11
threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
}
步骤2:
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {判断 value时是否为null,如果为null抛出异常
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table; 将 table 数组【本质是一个 Enty<?,?>[]数组】赋给 tab
int hash = key.hashCode();获取一个哈希值
int index = (hash & 0x7FFFFFFF) % tab.length;计算出在数组中索引位置
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];获取当前索引位置的元素是,是否为null,如果不为null,则进入for进行相关的判断
for(; entry != null ; entry = entry.next) {
如果要添加的元素和原来的元素 哈希值和equals值,相同时,则将原来的value替换掉,并返回
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
addEntry(hash, key, value, index);
return null;
}
调用addEntry 方法进行添加
private void addEntry(int hash, K key, V value, int index) {
modCount++;记录修改次数
Entry<?,?> tab[] = table;
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();
tab = table;
hash = key.hashCode();
index = (hash & 0x7FFFFFFF) % tab.length;
}
// Creates the new entry.
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
}
调用 Hashtable 类中静态内部类 Entry 完成初始化
private static class Entry<K,V> implements Map.Entry<K,V> {
final 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;
}
protected void rehash() {
int oldCapacity = table.length;记录当前表的长度
Entry<?,?>[] oldMap = table;并将表赋给 一个 oldMap 值
// overflow-conscious code
int newCapacity = (oldCapacity << 1) + 1;// oldCapacity乘2+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 数组
Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
modCount++;记录
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
table = newMap;
将其移动到新的 Entry 数组中
for (int i = oldCapacity ; i-- > 0 ;) {
for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
Entry<K,V> e = old;
old = old.next;
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = (Entry<K,V>)newMap[index];
newMap[index] = e;
}
}
}
Hashtable
扩容机制
package com.hspedu.map_;
import java.util.Hashtable;
@SuppressWarnings({"all"})
public class HashTableExercise {
public static void main(String[] args) {
Hashtable table = new Hashtable();//ok
table.put("john", 100); //ok
//table.put(null, 100); //异常 NullPointerException
//table.put("john", null);//异常 NullPointerException
table.put("lucy", 100);//ok
table.put("lic", 100);//ok
table.put("lic", 88);//替换
table.put("hello1", 1);
table.put("hello2", 1);
table.put("hello3", 1);
table.put("hello4", 1);
table.put("hello5", 1);
table.put("hello6", 1);
System.out.println(table);
//简单说明一下Hashtable的底层
//1. 底层有数组 Hashtable$Entry[] 初始化大小为 11
//2. 临界值 threshold 8 = 11 * 0.75
//3. 扩容: 按照自己的扩容机制来进行即可.
//4. 执行 方法 addEntry(hash, key, value, index); 添加K-V 封装到Entry
//5. 当 if (count >= threshold) 满足时,就进行扩容
//5. 按照 int newCapacity = (oldCapacity << 1) + 1; 的大小扩容.
}
}
Hashtable
和HashMap
对比
版本 | 线程安全【同步】 | 效率 | 允许null键null值 | |
---|---|---|---|---|
HashMap | 2.0 | 不安全 | 高 | 允许 |
Hashtable | 1.0 | 安全 | 较低 | 不允许 |
注意是:HashMap
键值都可以存储null
值,但是key
只能有一个,value
可以有多个 Hashtable
键值都不可以存储null
值