哈希表中泛型的键与红黑树中泛型的键本质是不一样的。
由于实现的树是二分搜索树,所以要求键key必须具有可比较性。
而对于哈希表是不需要这种性质的。
对哈希表中键key必须有一个要求:必须实现hashCode方法。
在Java中所有的类型的父类都是Object。而Object类中默认就有hashCode方法。所以所有类型默认都有实现hashCode的方法。
利用TreeMap(底层为红黑树)来实现哈希表:
import java.util.TreeMap;
public class HashTable<K, V> {
//利用TreeMap封装成一个数组
//数组:每个元素存放着键和值
private TreeMap<K, V>[] hashtable;
//哈希表中的元素个数
private int size;
//哈希表的长度
private int M;
public HashTable(int M){
this.M = M;
size = 0;
//开辟数组空间
hashtable = new TreeMap[M];
//对每一个TreeMap进行实例化
for(int i = 0 ; i < M ; i ++){
hashtable[i] = new TreeMap<>();
}
}
public HashTable(){
this(97);
}
//先将key转化成整型,消除符号
private int hash(K key){
return (key.hashCode() & 0x7fffffff) % M;
}
public int getSize(){
return size;
}
public void add(K key, V value){
//根据hash(key)索引取出hashtable中的元素
TreeMap<K, V> map = hashtable[hash(key)];
//看map中是否包含了key
if(map.containsKey(key)){
//修改该值
map.put(key, value);
}else{
//添加数据
map.put(key, value);
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 --;
}
return ret;
}
public void set(K key, V value){
TreeMap<K, V> map = hashtable[hash(key)];
if(!map.containsKey(key)){
throw new IllegalArgumentException(key + " doesn't exist!");
}
map.put(key, value);
}
public boolean contains(K key){
return hashtable[hash(key)].containsKey(key);
}
public V get(K key){
return hashtable[hash(key)].get(key);
}
}
对哈希表和红黑树的查找性能进行比较:
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
System.out.println("Pride and Prejudice");
ArrayList<String> words = new ArrayList<>();
if(FileOperation.readFile("pride-and-prejudice.txt", words)) {
System.out.println("Total words: " + words.size());
// Test RBTree
startTime = System.nanoTime();
RBTree<String, Integer> rbt = new RBTree<>();
for (String word : words) {
if (rbt.contains(word)){
rbt.set(word, rbt.get(word) + 1);
}else{
rbt.add(word, 1);
}
}
for(String word: words){
rbt.contains(word);
}
endTime = System.nanoTime();
time = (endTime - startTime) / 1000000000.0;
System.out.println("RBTree: " + time + " s");
// Test HashTable
startTime = System.nanoTime();
// HashTable<String, Integer> ht = new HashTable<>();
HashTable<String, Integer> ht = new HashTable<>(131071);
for (String word : words) {
if (ht.contains(word)){
ht.set(word, ht.get(word) + 1);
}else{
ht.add(word, 1);
}
}
for(String word: words){
ht.contains(word);
}
endTime = System.nanoTime();
time = (endTime - startTime) / 1000000000.0;
System.out.println("HashTable: " + time + " s");
}
System.out.println();
}
}
测试发现哈希表更快一些:
很多时候我们并不知道要存储多少元素,所以就要对哈希表的时间复杂度进行分析。