哈希映射直接访问运算符[]

时间:2014-08-30 15:39:57

标签: c++ c++11 stl hashtable unordered-map

我已经实现了哈希表(std::unordered_map):

#include <bits/stdc++.h>
#ifndef HASH_TABLE_H
#define HASH_TABLE_H

#define TABLE_SIZE 100

// Hash node class template
template <typename K, typename V>
class HashNode {
public:
    HashNode(const K &key, const V &value)
        : key(key)
        , value(value)
        , next(NULL) {
    }

    K getKey() const {
        return key;
    }

    V getValue() const {
        return value;
    }

    void setValue(V value) {
        HashNode::value = value;
    }

    HashNode *getNext() const {
        return next;
    }

    void setNext(HashNode *next) {
        HashNode::next = next;
    }
private:
    // key-value pair
    K key;
    V value;
    // next bucket with the same key
    HashNode *next;
};

// Default hash function class
template <typename K>
struct KeyHash {
    unsigned long operator()(const K& key) const {
        return reinterpret_cast<unsigned long>(key) % TABLE_SIZE;
    }
};

// Hash map class template
template <typename K, typename V, typename F = KeyHash<K>>
class HashTable {
public:
    HashTable() {
        // construct zero initialized hash table of size
        table = new HashNode<K, V> *[TABLE_SIZE]();
    }

    ~HashTable() {
        // destroy all buckets one by one
        for (int i = 0; i < TABLE_SIZE; ++i) {
            HashNode<K, V> *entry = table[i];
            while (entry != NULL) {
                HashNode<K, V> *prev = entry;
                entry = entry->getNext();
                delete prev;
            }
            table[i] = NULL;
        }
        // destroy the hash table
        delete [] table;
    }

    bool get(const K &key, V &value) {
        unsigned long hashValue = hashFunc(key);
        HashNode<K, V> *entry = table[hashValue];
        while (entry != NULL) {
            if (entry->getKey() == key) {
                value = entry->getValue();
                return true;
            }
            entry = entry->getNext();
        }
        return false;
    }

    void put(const K &key, const V &value) {
        unsigned long hashValue = hashFunc(key);
        HashNode<K, V> *prev = nullptr;
        HashNode<K, V> *entry = table[hashValue];
        while (entry != nullptr and entry->getKey() != key) {
            prev = entry;
            entry = entry->getNext();
        }
        if (entry == nullptr) {
            entry = new HashNode<K, V>(key, value);
            if (prev == nullptr) {
                // insert as first bucket
                table[hashValue] = entry;
            } else {
                prev->setNext(entry);
            }
        } else {
            // just update the value
            entry->setValue(value);
        }
    }

    // direct access operator overloading 
    V& operator [] (const K& key) {
        V value;
        get(key, value);
        return value;
    }

    void remove(const K &key) {
        unsigned long hashValue = hashFunc(key);
        HashNode<K, V> *prev = nullptr;
        HashNode<K, V> *entry = table[hashValue];
        while (entry != nullptr and entry->getKey() != key) {
            prev = entry;
            entry = entry->getNext();
        }
        if (entry != nullptr) {
            if (prev == nullptr) {
                // remove first bucket of the list
                table[hashValue] = entry->getNext();
            } else {
                prev->setNext(entry->getNext());
            }
            delete entry;
        }
    }

private:
    // hash table
    HashNode<K, V> **table;
    F hashFunc;
};

#endif // HASH_TABLE_H

struct MyKeyHash {
    unsigned long operator()(const int& k) const {
        return k % 10;
    }
};

int main(void) {
    HashTable<int, std::string, MyKeyHash> hmap;
    hmap.put(1, "val1");
    hmap.put(2, "val2");
    hmap.put(3, "val3");
    std::string value;
//    hmap.get(2, value);
    std::cout << hmap[2] << std::endl; // val2
//    hmap[2] = "val_new";
//    std::cout << hmap[2] << std::endl; // val2
    bool res = hmap.get(3, value);
    if (res)
        std::cout << value << std::endl; // val3
    hmap.remove(3);
    res = hmap.get(3, value);
    if (res)
        std::cout << value << std::endl; // nothing
    return 0;
}

问题是直接访问操作符[]重载不能正常工作。我可以通过hmap[1]访问密钥,因为函数签名是V& operator [] (const K& key)但我不能像hmap[1] = "something"一样分配,因为它没有返回任何指针,其中值将被覆盖。我怎样才能实现这两种功能?

1 个答案:

答案 0 :(得分:3)

变化:

V& operator [] (const K& key) {
    V value;
    get(key, value);
    return value;
}

成:

V& operator [] (const K& key) {
    unsigned long hashValue = hashFunc(key);
    HashNode<K, V> *entry = table[hashValue];
    while (entry != NULL) {
        if (entry->getKey() == key) {
            return entry->getValue();
        }
        entry = entry->getNext();
    }
    // alternatively, as suggested by NetVipeC, you can return
    // here a reference to default-constructed element under given key
    // e.g.: put(key, V{}); return (*this)[key]; 
    throw std::range_error{"Key not found!"};
}

并使HashNode::getValue返回引用:

V& getValue() {
    return value;
}

const V& getValue() const {
    return value;
}