【问题标题】:hash map direct access operator []哈希映射直接访问运算符 []
【发布时间】:2014-10-24 09:32:19
【问题描述】:

我已经实现了一个哈希表 (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&amp; operator [] (const K&amp; key),但我不能像hmap[1] = "something" 那样分配,因为它没有返回任何值将被覆盖的指针。如何实现这两个功能?

【问题讨论】:

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


    【解决方案1】:

    变化:

    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;
    }
    

    【讨论】:

    • 通常这种类(map、set 等)的默认行为是,如果键不存在,则插入,以避免抛出异常。
    • 在map、set、unordered_map等的情况下,这种行为是标准规定的,在他特定的hash map实现中,可以做任何他想做的事,但我建议遵循尽可能多地推荐标准。最好在自定义容器中使用算法和其他 STL 成员。
    • @NetVipeC:当然,我更新了我的代码以包含您的建议
    • +1 是的。我在等你的编辑put(key, V{}); return (*this)[key]; :)
    • @PiotrS。不仅仅是实现细节。异常非常慢,并且由于使用哈希表来提高性能,因此您应该能够在没有异常风险的情况下使用它们。因此,任何哈希表都应该提供一种无异常方式来检查是否包含一个键,一种无异常方式来显式创建和设置一个不预先存在的条目(否则返回错误失败),以及无异常方式设置(可能创建)条目的方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-15
    • 1970-01-01
    • 2011-08-26
    • 2013-05-14
    • 2018-10-13
    • 2016-01-31
    相关资源
    最近更新 更多