实现一个自己的哈希表Hashtable(数组+TreeMap)

这个哈希表的实现是相关数据结构的学习记录,

 简单实现了一个哈希表,跟Java api中的Hashtable是不相同的,这个哈希表底层用的是数组和TreeMap,对于这个哈希表来说,有几个比较重要的地方,首先是hash值的运算方法,采用的是模除哈希表的长度M

   

     // 计算每个key对应的hash值
    private int hash(K key) {
        return (key.hashCode() & 0x7fffffff) % M;
    }

哈希表的容量,即M,这是一个避免哈希冲突的关键量,本类使用的是一个capacity数组(内部的数据是从相关网站上搜集的被测试可以有效避免hash冲突的素数),这个数组当中的每一个值在每次数组扩容跟缩容的时候使用。

然后是哈希表发生哈希冲突的平均负载量(哈希表中实际存储元素处于哈希表的长度),本类定义两个静态常量,upperTol与lowTol,当平均负载大于upperTol时进行扩容,在小于lowTol时进行缩容

其他需要注意的地方在代码的注释中有更详细的说明

import java.util.ArrayList;
import java.util.TreeMap;

public class HashTable<K, V> {
	// 如果哈希表的平均负载量达到upperTol,则要进行扩容
	private static final int upperTol = 10;
	// 如果哈希表的平均负载量小于lowTol,则要进行缩容
	private static final int lowTol = 2;
	// capacity数组中存的为每次扩容或者缩容的哈希表长度
	private final int[] capacity = { 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317, 196613,
			393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653, 100663319, 201326611, 402653189,
			805306457, 1610612741 };
	// capacityIndex为指向数组元素的索引
	private int capacityIndex = 0;
	// 定义一个TreeMap类型的数组hashtable
	private TreeMap<K, V>[] hashtable;
	private int size;
	// M为哈希表的长度
	private int M;

	// hash表的初始化,有参构造,用户可自定义哈希表的长度
	public HashTable() {
		this.M = capacity[capacityIndex];
		size = 0;
		hashtable = new TreeMap[M];
		// 初始化哈希表中的每一个map
		for (int i = 0; i < M; i++) {
			hashtable[i] = new TreeMap<>();
		}

	}

	// 计算每个key对应的hash值
	private int hash(K key) {
		return (key.hashCode() & 0x7fffffff) % M;
	}

	public void add(K key, V value) {
		// 计算key的hash值,将此值对应的hash表中的map取出来
		TreeMap<K, V> map = hashtable[hash(key)];
		// 判断此map是否含有key对应值
		if (map.containsKey(key))
			// 如果包含直接添加
			map.put(key, value);
		else {
			// 否则,添加该值
			map.put(key, value);
			size++;
		}

		// 如果size/M>upperTol时进行扩容,这里将除法转化为乘法,避免了浮点类型的转换,
		// 扩容之前要对capacityIndex的合法性进行判断,以免发生溢出
		if (size > M * upperTol && capacityIndex + 1 < capacity.length)
			capacityIndex++;
		resize(capacity[capacityIndex]);
	}

	// 改变数组容量
	private void resize(int newM) {
		// TODO Auto-generated method stub

		TreeMap<K, V>[] newHashtable = new TreeMap[newM];
		// 初始化新数组中的每个map
		for (int i = 0; i < newM; i++) {
			newHashtable[i] = new TreeMap<>();
		}
		// 由于在下面有hash操作,模除的是新的M,则需要对M进行更新操作,同时,还要保留旧的M.
		int oldM = M;
		this.M = newM;
		// 循环遍历旧数组,取出原有的map
		for (int i = 0; i < oldM; i++) {
			TreeMap<K, V> map = hashtable[i];
			// 对于每一个map,取出其key的集合,将其中key对应的元素一个一个放入新的map中
			for (K key : map.keySet()) {
				newHashtable[hash(key)].put(key, map.get(key));
			}
		}
		// 将当前newHashtable指向hashtable
		this.hashtable = newHashtable;
	}

	public int Size() {
		return size;
	}

	public V remove(K key) {
		V ret = null;
		TreeMap<K, V> map = hashtable[hash(key)];
		if (map.containsKey(key)) {
			ret = map.remove(key);
			size--;
		}
		if (size < M * lowTol && capacityIndex - 1 > 0)
			capacityIndex--;
		resize(capacity[capacityIndex]);
		return ret;
	}

	public void set(K key, V value) {
		TreeMap<K, V> map = hashtable[hash(key)];
		if (!map.containsKey(key))
			throw new IllegalArgumentException("no such key");
		map.put(key, value);
	}

	public boolean contains(K key) {
		return hashtable[hash(key)].containsKey(key);
	}

	public V get(K key) {
		TreeMap<K, V> map = hashtable[hash(key)];
		V value = map.get(key);
		return value;
	}
}

       

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值