开放地址法实现HashTable

前注:本文不是讲解Java类库的Hashtable实现原理,而是根据计算机哈希表原理自己实现的一个Hashtable。

HashTable内部是用数组存放一个(Key-Value pair)键值对的引用,其原理是根据Key的hashCode来计算出数组下标。因为存放位置是直接计算出来,不需要遍历数据结构,这使得hash table具有快速增删查改的优势。下面介绍HashTable的实现步骤:

  • 取得Key的hashCode。

  通过Eclipse等工具可以轻松的复写Object的hashCode方法,生成的hashCode是不会有重复值的。

  • 将hashCode与地址映射(压缩地址):

  但是,Hashtable内部是用一个数组来存储Key-Value对的,数组容量有限,本文中设置数组容量为10。那么现在的问题就是如何将hashCode和数组地址相对应?一个简单的方法就是对hashCode取模运算(hashCode%10)。这样就可以将hashCode与数组地址做映射(多对一)。同时注意,hashCode可能为负数,而数组的下标不能为负数,所以在映射的时候需要处理一下。

1 int hashCode =  Math.abs(key.hashCode())% this.maxSize;
  • 解决hashCode重复问题:

上文已经说过,数组容量为10,当我们在将hashCode映射为数组下标的时候,肯定会遇到有重复的情况。比如hashCode为21和31,分别对他们取模运算,结果都是1,也就是说这两个hashCode对应数组的下标都是1.那么第一个元素添加上去后,再添加第二个元素,则新元素会覆盖之前的元素。这时,如果我们想后面添加的重复地址元素也能添加上去,只能将其放在其它位置。这时,我们可以将新元素放在改地址的下一位,如果下一位已经有元素,那么就继续往后找,直到找到空位为止(其实这个过程有些边界条件需要考虑,比如找到数组末尾后应跳到数组开头继续找。以及数组已经满了,找遍数组都找不到合适的位置,就应该提示该数组已满,不能插入)。

现在既然解决了上面的问题,也就是说每个元素都能在数组中找到合适的位置(除非数组已满)。那么可以根据这个算法实现数组的增删查改。下面是Hashtable的实现代码:

  1 package org.lyk.impl;
  2
  3 public class HashTable<K, V>
  4 {
  5     /**
  6      * Key-Value pair 存放键值对
  7      * @author liuyuank
  8      *
  9      */
 10     private class KeyValue
 11     {
 12         K key;
 13         V value;
 14
 15         private KeyValue(K key, V value)
 16         {
 17             this.key = key;
 18             this.value = value;
 19         }
 20
 21         public K getKey()
 22         {
 23             return key;
 24         }
 25
 26         public void setKey(K key)
 27         {
 28             this.key = key;
 29         }
 30
 31         public V getValue()
 32         {
 33             return value;
 34         }
 35
 36         public void setValue(V value)
 37         {
 38             this.value = value;
 39         }
 40
 41     }
 42
 43     private Object[] table;
 44     private int maxSize = 10;
 45     private int currentAmmount = 0;
 46
 47     public HashTable()
 48     {
 49         this.table = new Object[this.maxSize];
 50     }
 51
 52     public HashTable(int maxSize) throws Exception
 53     {
 54         if (0 == maxSize || maxSize < 0 || maxSize > 100)
 55         {
 56             throw new Exception("table容量非法!");
 57         }
 58
 59         this.maxSize = maxSize;
 60         this.table = new Info[maxSize];
 61     }
 62
 63     /**
 64      * 增加一个键值对
 65      * @param key
 66      * @param value
 67      */
 68     public void add(K key, V value)
 69     {
 70         //将hashCode映射到数组下标
 71         int hashCode =  Math.abs(key.hashCode())% this.maxSize;
 72
 73         //将元素插入到数组中,如果该位置已经被占用,则循环查找下一个位置,直到找到合适的位置,或发现数组已满,退出循环
 74         while (this.table[hashCode] != null
 75                 && (this.currentAmmount < this.maxSize))
 76         {
 77             hashCode++;
 78             hashCode = hashCode % this.maxSize;
 79         }
 80
 81         if (this.currentAmmount == this.maxSize)
 82         {
 83             //数组已满
 84             System.out.println("Hash table 已满");
 85         } else
 86         {
 87             //找到合适位置
 88             this.table[hashCode] = new KeyValue(key, value);
 89             this.currentAmmount++;
 90         }
 91     }
 92
 93     /**
 94      * 与add方法同样的算法,根据key值找到数组中元素,然后将改元素设置为null
 95      * @param key
 96      * @return
 97      */
 98     public boolean remove(K key)
 99     {
100         int hashCode = Math.abs(key.hashCode()) % this.maxSize;
101         int count = 0;
102         while (this.table[hashCode] != null && count < this.maxSize)
103         {
104             if (((KeyValue) this.table[hashCode]).getKey().equals(key))
105             {
106                 this.table[hashCode] = null;
107                 return true;
108             }
109             count++;
110             hashCode++;
111             hashCode = hashCode%this.maxSize;
112         }
113
114         return false;
115     }
116
117     public V get(K key)
118     {
119         int hashCode = Math.abs(key.hashCode()) % this.maxSize;
120         int count = 0;
121         while (this.table[hashCode] != null && count < this.maxSize)
122         {
123             if (key.equals(((KeyValue)this.table[hashCode]).getKey()))
124                 return ((KeyValue) this.table[hashCode]).getValue();
125
126             hashCode++;
127             count++;
128             hashCode = hashCode%this.maxSize;
129         }
130         return null;
131     }
132
133     public boolean contains(K key)
134     {
135         if (this.get(key) != null)
136         {
137             return true;
138         } else
139         {
140             return false;
141         }
142     }
143
144     public void replace(K key, V value)
145     {
146         KeyValue kv = this.find(key);
147         if(kv != null)
148         {
149             kv.setValue(value);
150         }
151     }
152
153     private KeyValue find(K key)
154     {
155         int hashCode = Math.abs(key.hashCode()) % this.maxSize;
156         int count = 0;
157         while (this.table[hashCode] != null && count < this.maxSize)
158         {
159             if (key.equals(((KeyValue)this.table[hashCode]).getKey()))
160                 return ((KeyValue) this.table[hashCode]);
161
162             hashCode++;
163             count++;
164             hashCode = hashCode%this.maxSize;
165         }
166         return null;
167     }
168 }

HashTable实现

package org.lyk.impl;

import java.math.BigInteger;

public class Info
{
    private String name;
    private String address;
    private Integer age; 

    public Info(String name, String address, Integer age)
    {
        super();
        this.name = name;
        this.address = address;
        this.age = age;
    }
    @Override
    public int hashCode()
    {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((address == null) ? 0 : address.hashCode());
        result = prime * result + ((age == null) ? 0 : age.hashCode());
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

//    @Override
//    public int hashCode()
//    {
//        final int prime = 27;
//        int result = 1;
//        result = prime*result + (this.name == null ? 0:this.name.hashCode());
//        result = prime*result + (this.address == null ? 0:this.address.hashCode());
//        result = prime*result + (this.age == null ? 0 : this.age.hashCode());
//        return result;
//    }

    @Override
    public boolean equals(Object obj)
    {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Info other = (Info) obj;
        if (address == null)
        {
            if (other.address != null)
                return false;
        } else if (!address.equals(other.address))
            return false;
        if (age == null)
        {
            if (other.age != null)
                return false;
        } else if (!age.equals(other.age))
            return false;
        if (name == null)
        {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
    public String getName()
    {
        return name;
    }
    public void setName(String name)
    {
        this.name = name;
    }
    public String getAddress()
    {
        return address;
    }
    public void setAddress(String address)
    {
        this.address = address;
    }
    public Integer getAge()
    {
        return age;
    }
    public void setAge(Integer age)
    {
        this.age = age;
    }
    @Override
    public String toString()
    {
        return "Info [name=" + name + ", address=" + address + ", age=" + age
                + "]";
    }

}

Value实现

测试代码:String为Key Info为Value

package org.lyk.main;

import org.lyk.impl.BiTree;
import org.lyk.impl.HashTable;
import org.lyk.impl.Info;

public class Main
{
    public static void main(String[] args)
    {
        HashTable<String, Info> ht = new HashTable<>();
        for(int i =0; i <15;i++)
        {
            Info info = new Info("sheldon" + i, "address" + i , i);
            //System.out.println("hashCode in main:" + info.getName().hashCode());
            ht.add(info.getName(), info);
        }

        String key = "sheldon3";
        System.out.println(ht.contains(key));
        ht.replace(key, new Info("谢耳朵","美国洛杉矶", 999));
        System.out.println(ht.contains(key));
        System.out.println(ht.get(key));
        System.out.println("///~ main done");
    } 

}
时间: 2024-08-28 14:44:03

开放地址法实现HashTable的相关文章

算法学习 - HashTable开放地址法解决哈希冲突

开放地址法解决哈希冲突 线性开放地址法 线性开放地址法就是在hash之后,当发现在位置上已经存在了一个变量之后,放到它下一个位置,假如下一个位置也冲突,则继续向下,依次类推,直到找到没有变量的位置,放进去. 平方开放地址法 平方地址法就是在hash之后,当正确位置上存在冲突,不放到挨着的下一个位置,而是放到第2^0位置,假如继续冲突放到2^1的位置,依次2^3... 直到遇到不冲突的位置放进去. 双散列开放地址法 双散列同上,不过不是放到2^的位置,而是放到key - hash(key, tab

Java解决Hash(散列)冲突的四种方法--开放地址法(线性探测,二次探测,伪随机探测)、链地址法、再哈希、建立公共溢出区

最近时间有点紧,暂时先放参考链接了,待有时间在总结一下: 查了好多,这几篇博客写的真心好,互有优缺点,大家一个一个看就会明白了: 参考 1. 先看这个明白拉链法(链地址法),这个带源码,很好看懂,只不过是只讲了拉链法一种: 2. 再看这个比较全的,四种全讲了,链接,这篇比较形象,有图.但是这两篇都没有仔细介绍优缺点: 3. 最后看优缺点,点击这里: 原文地址:https://www.cnblogs.com/gjmhome/p/11372883.html

--算法分析与设计--课程作业--【顺序统计】--【采用链表法散列表】--【开放地址法(双重散列)】

本次作业大力感谢以下量 参考信息 经典算法总结之线性时间做选择 http://www.cnblogs.com/javaspring/archive/2012/08/17/2656208.html 11.4 双重散列法 : http://blog.csdn.net/zixiawzm/article/details/6746946 [未完待续]

链地址法实现HashMap

前注:本文介绍的HashMap并非Java类库的实现.而是根据哈希表知识的一个实现. 上文介绍了开放地址法实现HashTable,它的缺点是对hashCode映射为地址后如果出现重复地址,则会占用其他元素的位置.这样HashTable存储容量有限,而且不便于算法理解.本文介绍链地址法实现HashMap. 链地址法内部仍然有一个数组,但区别与开放地址法,该数组存储的是一个链表的引用.当根据hashCode计算出数组下表后,对元素的增删查改都是在该数组元素所指向的链表上完成的.这就解决了hashCo

哈希(hash) 之插入和查找(链地址法)

一:学些心得 1 getHash函数的设计最牛的是Unix中处理字符串的ELFHash():当然也可以自己写一个比较简单的getHash函数关键在于去mod M的M值,使器均匀的分布(一般是不大于hash_size的某一个素数,接近于2的某次幂):但是有一点需要注意就是返回的hash值必须是正值. 2 处理冲突的方法:链地址法是比较好的方法了(静态动态都可以的):二次哈希(一般是加key值)再探测:或者加1再探测 3 插入和查找以及删除的第一步都是一样的,getHash(),时候存在-- 4 对

哈希表——线性探测法、链地址法、查找成功、查找不成功的平均长度

一.哈希表 1.概念 哈希表(Hash Table)也叫散列表,是根据关键码值(Key Value)而直接进行访问的数据结构.它通过把关键码值映射到哈希表中的一个位置来访问记录,以加快查找的速度.这个映射函数就做散列函数,存放记录的数组叫做散列表. 2.散列存储的基本思路 以数据中每个元素的关键字K为自变量,通过散列函数H(k)计算出函数值,以该函数值作为一块连续存储空间的的单元地址,将该元素存储到函数值对应的单元中. 3.哈希表查找的时间复杂度 哈希表存储的是键值对,其查找的时间复杂度与元素数

C# Dictionary源码剖析---哈希处理冲突的方法有:开放定址法、再哈希法、链地址法、建立一个公共溢出区等

参考:https://blog.csdn.net/exiaojiu/article/details/51252515 http://www.cnblogs.com/wangjun1234/p/3719635.html 源代码版本为 .NET Framework 4.6.1 Dictionary是Hashtable的一种泛型实现(也是一种哈希表)实现了IDictionary泛型接口和非泛型接口等,将键映射到相应的值.任何非 null 对象都可以用作键.使用与Hashtable不同的冲突解决方法,D

哈希表(开放定址法处理冲突)(1013)

Description 采用除留余数法(H(key)=key %n)建立长度为n的哈希表,处理冲突用开放定址法的线性探测. Input 第一行为哈希表的长度n: 第二行为关键字的个数: 第三行为关键字集合: 第三行为要查找的数据. Output 如果查找成功,输出关键字所哈希表中的地址和比较次数:如果查找不成功,输出-1. 如果查找成功,输出关键字所哈希表中的地址和比较次数:如果查找不成功,输出-1. Sample Input   1 2 3 4 5 13 11 16 74 60 43 54 9

C++链地址法实现哈希表

哈希表,也叫散列表,是根据关键码值(Key value)而直接进行访问的数据结构.也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做哈希函数,存放记录的数组叫做哈希表. 哈希函数最主要的设计在于哈希函数和冲突处理的解决,其中哈希函数的设计方法主要有直接定址法和除留余数法:冲突处理的方法主要有开放定址法和链地址法.本文主要实现了一个基本存放字符串的哈希表,冲突处理采用链地址法. 代码如下: #include <iostream> #include<c