七,用代码来验证自己写的hash表以及性能分析(之前的rehash方法写错了,现在更正过来了!)
package cn.java1118;
/**
* 自己写的hashmap类
* @author acer
*
* @param <K>:关键字类
* @param <V>:数据域类
*/
public class MyHashMap04<K,V> {
//存放键值对的数组
private Entry<K,V>[] hashTable;
//当前容量的大小
private int numberOfEntries;
//装载因子
private static final double MAX_LOAD_FACTOR=0.75;
//集合初始化的一个大小
static final int DEFAULT_INITIAL_CAPACITY = 16;
public MyHashMap04(){
this(DEFAULT_INITIAL_CAPACITY);
}
public MyHashMap04(int tablesize){
//实例化数组的大小
hashTable = new Entry[tablesize];
//初始化的时候hash表里面什么都木有
this.numberOfEntries=0;
}
/**
* 补充哈希函数
* @param h:每一个对象都有的hashcode
* @return:另一种hashcode
*/
static int hash(int h) {
//直接copyJDK自带的这个算法
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
/**
* 根据key取得value值
* @param key:键
* @return:键所对应的值
*/
public V getValue(K key){
//要返回的值
V result = null;
//得到要key在数组的下标
int hash = hash(key.hashCode());
int index = indexFor(hash, hashTable.length);
//对键值对链表进行遍历
for (Entry<K,V> e = hashTable[index]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))){
return e.value;
}
}
return result;
}
/**
* 添加元素
* @param key
* @param value
* @return:添加成功则为null,否则返回之前的值
*/
public V add(K key,V value){
//键值为null时的处理方法
if (key == null)
return putForNullKey(value);
//返回值变量
V oldValue;
//得到这个键在数组中的下标
int hash = hash(key.hashCode());
int index = indexFor(hash, hashTable.length);
//看是否存在一样的键和hashcode
for (Entry<K,V> e = hashTable[index]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
//存在这样的键值对了
oldValue = e.value;
e.value = value;
return oldValue;
}
}
//插入一个键值对的时候当前的容量会+1
numberOfEntries++;
//进行插入
addEntry(hash, key, value, index);
return null;
}
/**
* 当插入的键位null时的处理
* @param value:值
* @return:插入成功则返回null,不成功则返回这个键原来所对应的值
*/
private V putForNullKey(V value) {
for (Entry<K,V> e = hashTable[0]; e != null; e = e.next) {
if (e.key == null) {
V oldValue = e.value;
e.value = value;
return oldValue;
}
}
numberOfEntries++;
addEntry(0, null, value, 0);
return null;
}
/**
* 插入键值对链表
* @param hash:hashcode
* @param key:键
* @param value:值
* @param bucketIndex:数组上的下标
*/
void addEntry(int hash, K key, V value, int bucketIndex) {
//将原来的保留
Entry<K,V> e = hashTable[bucketIndex];
//在原来的基础上添加
hashTable[bucketIndex] = new Entry<K,V>(hash, key, value, e);
if(!isHashTableTooFull()){
//看是否超出其装载因子
rehash();
}
}
//检验hash表的装载因子是否大于默认值
private boolean isHashTableTooFull() {
//装载因子是有大小限制的
if(numberOfEntries/hashTable.length>=MAX_LOAD_FACTOR){
return false;
}
return true;
}
/**
* 再hash的方法
*/
private void rehash() {
//保留原hash表
Entry<K, V>[] oldHashTable = hashTable;
int oldSize = hashTable.length;
//扩大为原来的两倍
int newSize = oldSize*2;
hashTable = new Entry[newSize];
numberOfEntries=0;
//还要遍历原hash表,即将原来放进来的数据重新插入到新的hash表中
for(int i=0;i<oldHashTable.length;i++){
Entry<K, V> e = oldHashTable[i];
if(e!=null){
do{
Entry<K, V> next = e.next;
int index = indexFor(hash(e.hash), newSize);
addEntry(e.hash, e.key, e.value, index);
e=next;
}while(e!=null);
}
}
}
/**
* 删除指定key的键值对
* @param key:键
* @return
*/
public V remove(Object key) {
Entry<K,V> e = removeEntryForKey(key);
return (e == null ? null : e.value);
}
/**
* 跟查找的过程差不多
* @param key
* @return
*/
final Entry<K,V> removeEntryForKey(Object key) {
int hash = (key == null) ? 0 : hash(key.hashCode());
int i = indexFor(hash, hashTable.length);
Entry<K,V> prev = hashTable[i];
Entry<K,V> e = prev;
while (e != null) {
Entry<K,V> next = e.next;
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
numberOfEntries--;
if (prev == e)
hashTable[i] = next;
else
prev.next = next;
return e;
}
prev = e;
e = next;
}
return e;
}
/**
* 得到在数组中的下标位置
* @param h:hashcode
* @param length:hash表的长度
* @return:下标位置
*/
static int indexFor(int h, int length) {
return h & (length-1);
}
/**
* 实例化一个装键值对链表
* @author acer
*
* @param <K>
* @param <V>
*/
private class Entry<K,V>{
private K key;
private V value;
//每一个对象都有一个hashcode,是唯一的,在map中不能存放hashcode一样的对象
private int hash;
//下一个键值对节点
private Entry<K,V> next;
private Entry(int hash,K key,V value,Entry<K,V>next){
this.key = key;
this.value = value;
this.next = next;
this.hash = hash;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
//重写equals方法
public final boolean equals(Object o) {
if (!(o instanceof Entry))
return false;
Entry e = (Entry)o;
//得到两个比较的key值
Object k1 = getKey();
Object k2 = e.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
//得到两个比较的value值
Object v1 = getValue();
Object v2 = e.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2)))
return true;
}
return false;
}
}
}
性能分析代码:
package cn.java1118;
import java.util.HashMap;
public class Test1 {
public static void main(String[] args) {
//自己的hashmap
MyHashMap04<String, String> mm = new MyHashMap04<String, String>();
Long aBeginTime=System.currentTimeMillis();
for(int i=0;i<1000000;i++){
mm.add(""+i, ""+i*100);
}
Long aEndTime=System.currentTimeMillis();//记录EndTime
System.out.println("insert time-->"+(aEndTime-aBeginTime));
Long lBeginTime=System.currentTimeMillis();//记录BeginTime
mm.getValue(""+100000);
Long lEndTime=System.currentTimeMillis();//记录EndTime
System.out.println("seach time--->"+(lEndTime-lBeginTime));
Long rBeginTime=System.currentTimeMillis();//记录BeginTime
mm.remove(""+10000);
Long rEndTime=System.currentTimeMillis();//记录EndTime
System.out.println("remove time--->"+(rEndTime-rBeginTime));
//系统的hashmap
// HashMap<String, String> mm = new HashMap<String, String>();
// Long aBeginTime=System.currentTimeMillis();//记录BeginTime
// for(int i=0;i<1000000;i++){
// mm.put(""+i, ""+i*100);
// }
// Long aEndTime=System.currentTimeMillis();//记录EndTime
// System.out.println("insert time-->"+(aEndTime-aBeginTime));
//
// Long lBeginTime=System.currentTimeMillis();//记录BeginTime
// mm.get(""+100000);
// Long lEndTime=System.currentTimeMillis();//记录EndTime
// System.out.println("seach time--->"+(lEndTime-lBeginTime));
//
// Long rBeginTime=System.currentTimeMillis();//记录BeginTime
// mm.remove(""+10000);
// Long rEndTime=System.currentTimeMillis();//记录EndTime
// System.out.println("remove time--->"+(rEndTime-rBeginTime));
}
}
系统的测试结果:
insert time-->2148
seach time--->0
我的测试结果:
insert time-->2236
seach time--->0
remove time--->0
看,查找的速度为0呃,插入也只要2秒多。。。
还有一个是存储空间的测试:
<!--EndFragment-->