【Java 集合】HashTable 解析

一、概述

什么是哈希表?

据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。

什么是Hashtable类?

This class implements a hash table, which maps keys to values. Any non-null object can be used as a key or as a value.

Hashtable类实现了键值对结构的哈希表。任何键/值不为null的元素都允许存在。

Hashtable类如何保证线程安全?

Hashtable类 通过 synchronized 关键字 保证其不会被多个线程同时更改保证。

1. Dectionary 抽象类

Dictionary类是废弃类,已知子类有HashTable。新类均实现Map接口。

核心代码:

public abstract
class Dictionary<K,V> {
	/* 无参构造方法 */
    public Dictionary() {
    }
	
    /*  */
    abstract public int size();
    abstract public boolean isEmpty();
    abstract public Enumeration<K> keys();
    abstract public Enumeration<V> elements();
    abstract public V get(Object key);
    abstract public V put(K key, V value);
    abstract public V remove(Object key);
}

2. Map 接口

Map是存放键值对的数据结构。Map中没有重复的key,每个key最多只能映射一个value。Map不允许包含自身引用作为key。Map接口用来代替Dictionary抽象类。

Map接口提供了三种集合视图。map默认顺序是其集合视图迭代器遍历元素的顺序,例如HashMap。TreeMap自定义顺序。

核心代码:

public interface Map<K, V> {
	/* Query Operations */
    //the number of key-value mappings in this map:Map映射中K-V元素个数
    int size();
    
    //if this map contains no key-value mappings:Map是否未包含任何K-V元素
    boolean isEmpty();
    
    //if this map contains a mapping for the specified key:Map是否包含特定的K键
    boolean containsKey(Object key);
    
    //if this map maps one or more keys to the specified value:Map是否包含特定的V值
    boolean containsValue(Object value);
    
    //根据K获取元素
    V get(Object key);
    
    /* Modification Operations */
    //放入K-V元素
    V put(K key, V value);
    
    //依据K值移除元素
    V remove(Object key);
    
    //将旧Map元素放入新Map中
    void putAll(Map<? extends K, ? extends V> m);
    
    //清空Map映射
    void clear();
    
    //a set view of the keys contained in this map:返回Map中所有K构成的集(Set)
    Set<K> keySet();
    
    //a collection view of the values contained in this map:返回Map中所有V构成的容器(Collection)
    Collection<V> values();
    
    //a set view of the mappings contained in this map:返回Map中所有K-V元素构成的集(Set)
    Set<Map.Entry<K, V>> entrySet();
    
    /* 内部接口:Entry<K, V> */
    interface Entry<K, V> {
        
        //获取K值
        K getKey();
        
        //获取V值
        V getValue();
        
        //设置V值
        V setValue(V value);
        
        //重写equals方法
        boolean equals(Object o);
        
        // the hash code value for this map entry:重写hashCode方法,计算其hash值
        int hashCode();
        
        
        //.......(更多Defaut方法) 
    }
}

二、代码

1. 重要属性

属性说明初始值
tableThe hash table data.
hashtable桶数组。
长度:11
countThe total number of entries in the hash table.
Hashtable中的所有元素数。
0
thresholdThe table is rehashed when its size exceeds this threshold. (The * value of this field is (int)(capacity * loadFactor).)
扩容阈值,当表的大小超过此阈值时,将对该表进行扩容 (rehash) 操作。
11*0.75f
loadFactorThe load factor for the hashtable.
Hashtable负载因子。
0.75f
modCountThe number of times this Hashtable has been structurally modified Structural modifications are those that change the number of entries in the Hashtable or otherwise modify its internal structure (e.g., rehash).
Hashtable结构/数据发生修改的次数。
0
keySetKey键组成的集合。/
entrySetK-V键值对组成的集合。/

2. 重要方法

put/putIfAbsent

//put方法:synchronized关键字修饰,保证互斥:若已存在相同key值,则覆盖原元素
public synchronized V put(K key, V value) {
    // Make sure the value is not null
    if (value == null) {	//不允许value为null
        throw new NullPointerException();
    }

    // Makes sure the key is not already in the hashtable.
    Entry<?,?> tab[] = table;		//桶数组
    int hash = key.hashCode();		//键的hashcode,用于寻找桶索引
    int index = (hash & 0x7FFFFFFF) % tab.length;	//根据目前桶长度计算索引
    @SuppressWarnings("unchecked")
    Entry<K,V> entry = (Entry<K,V>)tab[index];	//取出该位置的链表头元素
    /* 循环:遍历寻找元素 */
    for(; entry != null ; entry = entry.next) {
        /* 判断:存在hash相等,key也相等的元素? */
        if ((entry.hash == hash) && entry.key.equals(key)) {
            //替换该元素
            V old = entry.value;
            entry.value = value;
            return old;	//返回 旧元素
        }
    }
	//将元素添加至桶链表内(当前)
    addEntry(hash, key, value, index);
    return null;	//返回 null
}
//put方法:synchronized关键字修饰,保证互斥:若已存在相同key值,则保留原元素
@Override
public synchronized V putIfAbsent(K key, V value) {
    Objects.requireNonNull(value);

    // Makes sure the key is not already in the hashtable.
    //有可能该键还没有hashtable内
    Entry<?,?> tab[] = table;
    int hash = key.hashCode();	 //计算key的hashcode
    int index = (hash & 0x7FFFFFFF) % tab.length;	//计算索引
    @SuppressWarnings("unchecked")
    Entry<K,V> entry = (Entry<K,V>)tab[index];	//取出该位置的链表头元素
    /* 循环:遍历寻找元素 */
    for (; entry != null; entry = entry.next) {
        /* 判断:存在hash相等,key也相等的元素? */
        if ((entry.hash == hash) && entry.key.equals(key)) {
            V old = entry.value;
            /* 判断: old是否为空? */
            if (old == null) {
                entry.value = value;
            }
            return old;
        }
    }
	//将元素添加至桶链表内(当前)
    addEntry(hash, key, value, index);
    return null;	//返回 null
}
//addEntry方法:辅助 put方法、putIfAbsent方法 放入元素
private void addEntry(int hash, K key, V value, int index) {
    Entry<?,?> tab[] = table;	//获取数组引用
    /* 判断:达到扩容? */
    if (count >= threshold) {
        // Rehash the table if the threshold is exceeded
        //重新分配桶数组长度
        rehash();
        tab = table;
        //重新计算hashcode
        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);	// 根据新的参数在链表的头部添加新的entry(头插法)
    //元素数++
    count++;
    //更改数++
    modCount++;
}

get/getOrDefault

//get方法[Dictionary抽象类]:synchronized关键字修饰,保证互斥:获取元素
public synchronized V get(Object key) {
    Entry<?,?> tab[] = table;	//获取桶数组引用
    int hash = key.hashCode();	//计算key的hashcode
    int index = (hash & 0x7FFFFFFF) % tab.length;	//计算相应索引
    /* 循环:遍历查找相关元素 */
    for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
        /* 判断:存在hash相等,key也相等的元素? */
        if ((e.hash == hash) && e.key.equals(key)) {
            return (V)e.value;	//返回对应value
        }
    }
    return null;	//返回null
}
//getOrDefault方法[Map接口]:synchronized关键字修饰,保证互斥:获取元素,若不存在,则返回一个 默认value值
@Override
public synchronized V getOrDefault(Object key, V defaultValue) {
    V result = get(key);	//获取元素
    return (null == result) ? defaultValue : result;	//若为空,则返回一个默认value值
}

contains/containskey/containsValue

//contains方法:synchronized关键字修饰,保证互斥:判断 value值 是否存在
public synchronized boolean contains(Object value) {
    if (value == null) {	//不允许value为null
        throw new NullPointerException();
    }
	
    Entry<?,?> tab[] = table;		//获取数组引用
    /* 外循环:遍历数组 */
    for (int i = tab.length ; i-- > 0 ;) {
        /* 内循环:遍历链表 */
        for (Entry<?,?> e = tab[i] ; e != null ; e = e.next) {
            /* 判断:是否存在value相等元素 */
            if (e.value.equals(value)) {
                return true;	//存在,true
            }
        }
    }
    return false;	//不存在,false
}
//containsKey方法[Map接口]:synchronized关键字修饰,保证互斥:判断 key键 是否存在
public synchronized boolean containsKey(Object key) {
    Entry<?,?> tab[] = table;		//获取数组引用
    int hash = key.hashCode();		//计算key的hashcode
    int index = (hash & 0x7FFFFFFF) % tab.length;	//计算相应索引
    /* 循环:寻找是否存在对应的key值 */
    for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
        if ((e.hash == hash) && e.key.equals(key)) {
            return true;	//存在,返回true
        }
    }
    return false;	//不存在,返回false
}
//containsValue方法[Map接口]:判断 value值 是否存在(作用与contains方法相同 [直接调用])
public boolean containsValue(Object value) {
    return contains(value);
}

remove

//remove方法[Dictionary抽象类]:synchronized关键字修饰,保证互斥:根据key移除对应元素,返回value
public synchronized V remove(Object key) {
    Entry<?,?> tab[] = table;		//获取数组引用
    int hash = key.hashCode();		//计算key的hashcode值
    int index = (hash & 0x7FFFFFFF) % tab.length;	//计算相应索引
    @SuppressWarnings("unchecked")
    Entry<K,V> e = (Entry<K,V>)tab[index];		//取出该位置的链表头元素
    /* 循环:遍历,并记录上一个Entry、目前Entry */
    for(Entry<K,V> prev = null ; e != null ; prev = e, e = e.next) {
        /* 判断: */
        if ((e.hash == hash) && e.key.equals(key)) {
            /* 判断:前一个值不为空? */
            if (prev != null) {
                prev.next = e.next;		//不为空,则跨越接链
            } else {
                tab[index] = e.next;	//为空,将链表下一个元素作为链表头元素接入数组
            }
            /* 元素移出链表 */
            modCount++;		//修改数++
            count--;		//元素数--
            V oldValue = e.value;	//获取旧value值
            e.value = null;			//赋null值,被移除元素等待GC回收
            return oldValue;		//移除成功,返回旧value值
        }
    }
    return null;	//未能移除,返回null值
}
//remove方法[Map接口]:synchronized关键字修饰,保证互斥:根据key-value移除对应元素,返回真值
@Override
public synchronized boolean remove(Object key, Object value) {
    Objects.requireNonNull(value);	//不允许value为null(否则抛出NullPointerException异常)

    Entry<?,?> tab[] = table;		//获取数组引用
    int hash = key.hashCode();		//计算key的hashcode值
    int index = (hash & 0x7FFFFFFF) % tab.length;	//计算相应索引
    @SuppressWarnings("unchecked")
    Entry<K,V> e = (Entry<K,V>)tab[index];		//取出该位置的链表头元素
    /* 循环:遍历,并记录上一个Entry、目前Entry */
    for (Entry<K,V> prev = null; e != null; prev = e, e = e.next) {
        /* 判断:存在元素hash、key、value均相等? */
        if ((e.hash == hash) && e.key.equals(key) && e.value.equals(value)) {
            //存在满足条件元素
            /* 判断:前一个值不为空? */
            if (prev != null) {
                prev.next = e.next;		//不为空,则跨越接链
            } else {
                tab[index] = e.next;	//为空,将链表下一个元素作为链表头元素接入数组
            }
            e.value = null; // clear for gc(赋null值,被移除元素等待GC回收)
            modCount++;		//修改数++
            count--;		//元素数--
            return true;	//移除成功,返回true
        }
    }
    return false;	//未能移除,返回false
}

replace

//replace方法[Map接口]:synchronized关键字修饰,保证互斥:替换
@Override
public synchronized boolean replace(K key, V oldValue, V newValue) {
    Objects.requireNonNull(oldValue);	//不允许oldValue为null(否则抛出NullPointerException异常)
    Objects.requireNonNull(newValue);	//不允许newValue为null(否则抛出NullPointerException异常)
    Entry<?,?> tab[] = table;			//获取数组引用
    int hash = key.hashCode();			//计算key的hashcode
    int index = (hash & 0x7FFFFFFF) % tab.length;	//计算相应索引
    @SuppressWarnings("unchecked")
    Entry<K,V> e = (Entry<K,V>)tab[index];	//寻找对应桶格(数组元素位)
    /* 循环:遍历链表 */
    for (; e != null; e = e.next) {
        /* 判断:存在hash相等,key也相等的元素? */
        if ((e.hash == hash) && e.key.equals(key)) {
            /* 判断:存在value相等的元素? */
            if (e.value.equals(oldValue)) {//存在,替换之,返回true
                e.value = newValue;		
                return true;
            } else {
                return false;	//不存在,无法替换,返回false
            }
        }
    }
    return false;	//不存在,无法替换,返回false
}
//replace方法[Map接口]:synchronized关键字修饰,保证互斥:替换
@Override
public synchronized V replace(K key, V value) {
    Objects.requireNonNull(value);		//不允许value为null(否则抛出NullPointerException异常)
    Entry<?,?> tab[] = table;			//获取数组引用
    int hash = key.hashCode();			//计算key的hashcode
    int index = (hash & 0x7FFFFFFF) % tab.length;	//计算相应索引
    @SuppressWarnings("unchecked")
    Entry<K,V> e = (Entry<K,V>)tab[index];	//寻找对应桶格(数组元素位)
    /* 循环:遍历链表 */
    for (; e != null; e = e.next) {
        /* 判断:存在hash相等,key也相等的元素? */
        if ((e.hash == hash) && e.key.equals(key)) {	//存在,替换之,并返回旧值oldValue
            V oldValue = e.value;
            e.value = value;
            return oldValue;
        }
    }
    return null;	//不存在,无法替换,返回null
}

size

/**
  * Returns the number of keys in this hashtable.
  * 【synchronized关键字修饰,保证互斥】
  * 返回 HashTable 元素数量
  */
public synchronized int size() {
    return count;
}

isEmpty

/**
  * Tests if this hashtable maps no keys to values.
  * 【synchronized关键字修饰,保证互斥】
  * 判断 Hashtable 是否元素数为0(是否为空)
  */
public synchronized boolean isEmpty() {
    return count == 0;
}

clone

/**
  * Creates a shallow copy of this hashtable. All the structure of the
  * hashtable itself is copied, but the keys and values are not cloned.
  * This is a relatively expensive operation.
  *
  */
public synchronized Object clone() {
    Hashtable<?,?> t = cloneHashtable();		//clone的hashtable(浅拷贝)
    t.table = new Entry<?,?>[table.length];		//数组设空
    for (int i = table.length ; i-- > 0 ; ) {
        t.table[i] = (table[i] != null)
            ? (Entry<?,?>) table[i].clone() : null;
    }
    t.keySet = null;	//key集合置空
    t.entrySet = null;	//元素集合置空
    t.values = null;	//value集合置空
    t.modCount = 0;		//修改数=0
    return t;			//返回hashtable
}
/** Calls super.clone() */
/* 调用父类的clone()方法 */
final Hashtable<?,?> cloneHashtable() {
    try {
        return (Hashtable<?,?>)super.clone();
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError(e);
    }
}

浅(shallow)拷贝,它拷贝了一个空的Hashtable结构,但是其中引用到对象(包括键和值)没有进行拷贝。

测试:

/* 测试代码 */ 
Hashtable t1 = new Hashtable();				//原 Hashtable 对象
Hashtable t2 = ((Hashtable) t1.clone());	//克隆 Hashtable 对象
System.out.println("是否是同一个对象:" + (t1 == t2));	//引用判断
t1.put(2,3);		//原 Hashtable 放入元素
t2.put(1,2);		//克隆 Hashtable 放入元素
System.out.println("t1:" + t1);		//打印 原 Hashtable 元素
System.out.println("t2:" + t2);		//克隆 原 Hashtable 元素

结果:
结果

clear

/**
  * Clears this hashtable so that it contains no keys.
  * 【synchronized关键字修饰,保证互斥】
  * 清空 hashtable 内部所有元素
  */
public synchronized void clear() {
    Entry<?,?> tab[] = table;	//获取数组引用
    /* 循环:遍历所有元素 */
    for (int index = tab.length; --index >= 0; )
        tab[index] = null;	//简单地将数组置空
    modCount++;	//更改数++
    count = 0;	//元素数归零
}

※ rehash方法

protected void rehash() {
    int oldCapacity = table.length;		//记录旧数组长度
    Entry<?,?>[] oldMap = table;		//获取数组引用

    // overflow-conscious code
    int newCapacity = (oldCapacity << 1) + 1;	//计算新的数组长度:原长度 * 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<?,?>[] newMap = new Entry<?,?>[newCapacity];	//创建新数组

    modCount++;		//更改数++
    //重新计算扩容阈值
    threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
    table = newMap;		//更换数组
	
    /* 外循环: */
    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应尽量避免使用,单线程下可使用HashMap替代,多线程下可使用ConcurrentHashMap替代。

☯ 缺点

  1. Hashtable的实现中,大量使用了%,而不是HashMap中的位运算,性能较差
  2. 在多数情况下都能保证线程安全,但因大量使用 synchronized 关键字,大量增删改查操作时开销大
  3. Hashtable中的key和value都不允许出现null值,HashMap/ConcurrentHashMap可以

四、Map同步化

使用Collections.synchronizedMap(Map<K,V> m)方法可以获得一个同步后的Map结构,但因使用大量使用 synchronized 关键字,缺点与Hashtable类似,仍存在大量增删改查操作时开销大的问题。

Map<Integer, String> map = Collections.synchronizedMap(new HashMap<>());
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值