hashmap的C++實現

按照hashmap的基本原理用C++實現了簡單的基本功能,複雜的實現參考C++庫的源碼,C++最新的標準庫裏已經有以下四種基於hashtable的容器:

unordered_set (C++11) unordered_multiset (C++11) unordered_map (C++11) unordered_multimap (C++11)。具體參考:http://en.cppreference.com/w/

/*
 * HashMap.h
 * Author: luxiaoxun
 */

#ifndef HASHMAP_H_
#define HASHMAP_H_

#include <iostream>
using namespace std;

//List all the integer number no less than 57 total number is 28
//And each number is about half of its next number
static int prime[28] =
{
    57,        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
};

class HashMapUtil
{
public:
    static int find_NextPrimeNumber(int current)
    {
        //Find the next prime number by searching in the prime number list
        int i = 0;
        for( ; i < 28 ; i++ )
            if(current < prime[i])
                break;
        return prime[i];     //return the next larger prime.
    }
};

template <class Key, class Value, class HashFunc, class EqualKey>
class HashMap
{
private:
    template <class _Key, class _Value>
    class KeyNode
    {
        public:
        _Value  value;      //Store the value
        _Key    key;        //Store the keyword
        int    used;
        //if the type of Value/Key is your own class, make sure they can handle copy constructor and operator =
        KeyNode():used(0){}
        KeyNode(const KeyNode & kn)
        {
            value = kn.value;
            key = kn.key;
            used = kn.used;
        }
        KeyNode & operator=(const KeyNode & kn)
        {
            if(this == &kn) return *this;
            value = kn.value;
            key = kn.key;
            used = kn.used;
            return *this;
        }
    };

public:
    HashMap();
    ~HashMap();
    bool insert(const Key& hashKey, const Value& value);
    bool remove(const Key& hashKey);
    void rehash();  //use it when rehashing
    Value& find(const Key& hashKey);
    const Value& operator [](const Key& hashKey) const;
    Value& operator [](const Key& hashKey);

private:
    HashFunc hash;
    EqualKey equal;
    HashMapUtil hml;
    KeyNode<Key ,Value> *table;
    int size;    //current number of itmes
    int capacity;   //capacity of the array
    static const double loadingFactor;
    int findKey(const Key& hashKey);  //find the index of a key
};

template<class Key , class Value , class HashFunc , class EqualKey>
const double HashMap<Key, Value, HashFunc, EqualKey>::loadingFactor = 0.9;

template<class Key , class Value , class HashFunc , class EqualKey>
HashMap<Key, Value, HashFunc, EqualKey>::HashMap()
{
    hash = HashFunc();
    equal = EqualKey();
    hml = HashMapUtil();
    capacity = hml.find_NextPrimeNumber(0); //initialize the capacity with first primer 57
    //resize the table with capacity because an extra one is used
    //to return the NULL type of Value in the function find
    table = new KeyNode<Key,Value>[capacity+1];
    for(int i = 0 ; i < capacity ; i++)    //initialize the table
        table[i].used = 0;
    size = 0;
}

template<class Key, class Value, class HashFunc, class EqualKey>
HashMap<Key, Value, HashFunc, EqualKey>::~HashMap()
{
    delete []table;
}

template<class Key, class Value, class HashFunc, class EqualKey>
bool HashMap<Key, Value, HashFunc, EqualKey>::insert(const Key& hashKey, const Value& value)
{
    int index = hash(hashKey)%capacity;
    //cout<<"Index is "<<index<<endl;
    if(table[index].used == 1)  //the key-value's hash is unique
    {
        //cout<<"The key-value must be unique!"<<endl;
        return false;
    }
    table[index].used = 1;         //modify the KeyNode
    table[index].key = hashKey;
    table[index].value = value;

    size++;
    //if the table's size is too large ,then rehash it
    if (size >= capacity * loadingFactor)
        rehash();
    return true;
}

template<class Key, class Value, class HashFunc, class EqualKey>
void HashMap<Key, Value, HashFunc, EqualKey>::rehash()
{
    int pastsize = capacity;
    //create a new array to copy the information in the old table
    capacity = hml.find_NextPrimeNumber(capacity);
    KeyNode<Key,Value>* tmp = new KeyNode<Key,Value>[capacity];
    for(int i = 0 ; i < pastsize ; i++)
    {
        if(table[i].used == 1)       //copy the KeyNode into the tmp array
        {
            tmp[i] = table[i];
        }
    }
    delete []table; //release the memory of the old table

    table = new KeyNode<Key,Value>[capacity+1];   //resize the table
    for(int i = 0 ; i < capacity ; i++) //initialize the table
    {
        table[i].used = 0;
    }
    for(int i = 0 ; i < pastsize ; i++) //insert the item into the table one by one
    {
        if(tmp[i].used == 1)
            insert(tmp[i].key, tmp[i].value);
    }
    delete []tmp;               //delete the tmp array
}

template<class Key, class Value, class HashFunc, class EqualKey>
bool HashMap<Key, Value, HashFunc, EqualKey>::remove(const Key& hashKey)
{
    int index = findKey(hashKey); //find the index of the key
    if(index < 0) //if find modify the flag with 0,else print out "no such key!"
    {
        cout<<"No such Key!"<<endl;
        return false;
    }
    else
    {
        table[index].used = 0;
        size--;
        return true;
    }
}

template<class Key, class Value, class HashFunc, class EqualKey>
Value& HashMap<Key, Value, HashFunc, EqualKey>::find(const Key& hashKey)
{
    int index = findKey(hashKey);
    if(index < 0) //if index <0 ,not found,else return the index
    {
        cout<<"can not find the key!"<<endl;
        return table[capacity].value; //return NULL
    }
    else
    {
        return table[index].value;
    }
}

template<class Key, class Value, class HashFunc, class EqualKey>
const Value& HashMap<Key, Value, HashFunc, EqualKey>::operator[](const Key& hashKey) const
{
    return find(hashKey); //overload the operation to return the value of the element
}

template<class Key, class Value, class HashFunc, class EqualKey>
Value& HashMap<Key, Value, HashFunc, EqualKey>::operator[](const Key& hashKey)
{
    return find(hashKey); //overload the operation to return the value of the element
}

template<class Key, class Value, class HashFunc, class EqualKey>
int HashMap<Key, Value, HashFunc, EqualKey>::findKey(const Key& hashKey)
{
    int index = hash(hashKey)%capacity;
    if ((table[index].used != 1) || !equal(table[index].key,hashKey))
        return -1;
    else
        return index;
}

#endif /* HASHMAP_H_ */

這裏實現的key必須是unique的,否則就要處理衝突的問題,可以通過[]操作修改對應的value,但是不能通過[]添加value,標準庫裏的是可以的。

C++編譯器不支持模板頭文件和實現代碼分離的編譯,如果的類實現和類聲明分別放在cpp文件和h頭文件裏,那麼在測試代碼裏include要加上實現的代碼,比如加上#include"HashMap.cpp"。使用hashmap,需要自己提供針對key的hash函數對象和equal函數對象,測試代碼:

#include "HashMap.h"
#include <string>
#include <iostream>
using namespace std;

//Hash function you provided must be correspond to the type of the Key
class HashFunc
{
public:
    int operator()(const string & key )
    {
        int hash = 0;
        for(int i = 0; i < key.length(); ++i)
        {
            hash = hash << 7 ^ key[i];
        }
        return (hash & 0x7FFFFFFF);
    }
};

//Equal function you provided to check whether two Keys are equal
//must be correspond to the type of the Key
class EqualKey
{
public:
    bool operator()(const string & A ,const string & B)
    {
        if(A.compare(B) == 0)
            return true;	//if equal return true
        else
            return false;	//else false
    }
};

int main()
{
    HashMap<string,string,HashFunc,EqualKey> hm;

    hm.insert("hello" , "you");
    hm.insert("why" , "dream");
    hm.insert("java" ,"good");
    hm.insert("welcome" ,"haha");

    hm.insert("welcome" ,"hehe"); //error, key-value must be unique


    cout<<"after insert:"<<endl;
    cout<<hm.find("welcome")<<endl;
    cout<<hm.find("java")<<endl;
    cout<<hm["why"]<<endl;
    cout<<hm["hello"]<<endl;

    if(hm.remove("hello"))
        cout<<"remove is ok"<<endl;	//remove is ok
    cout<<hm.find("hello")<<endl; //not exist print NULL

    hm["why"] = "love"; //modify the value 
    cout<<hm["why"]<<endl;

    return 0;
}


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章