Created
October 23, 2015 22:47
-
-
Save goldsborough/3ac9b7ed75dc53a970b6 to your computer and use it in GitHub Desktop.
Yet another hashmap :)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
template<typename Key, typename Value> | |
class Dictionary | |
{ | |
public: | |
using size_t = std::size_t; | |
using pre_hash_t = std::function<size_t(const Key&)>; | |
static const size_t minimum_capacity = 20; | |
Dictionary(size_t capacity = minimum_capacity, | |
const pre_hash_t& pre_hash = std::hash<Key>()) | |
: _nodes(new Node*[capacity]) | |
, _size(0) | |
, _capacity(capacity/_load_factor) | |
, _threshold(capacity) | |
, _pre_hash(pre_hash) | |
{ | |
std::fill(_nodes, _nodes + _capacity, nullptr); | |
} | |
Dictionary(std::initializer_list<std::pair<Key, Value>> list, | |
size_t capacity = minimum_capacity, | |
const pre_hash_t& pre_hash = std::hash<Key>()) | |
: Dictionary(std::max(list.size(), capacity), pre_hash) | |
{ | |
for (const auto& item : list) | |
{ | |
insert(item.first, item.second); | |
} | |
} | |
Dictionary(const Dictionary& other) | |
: _nodes(new Node*[other._capacity]) | |
, _size(other._size) | |
, _capacity(other._capacity) | |
, _threshold(other._threshold) | |
, _pre_hash(other._pre_hash) | |
{ | |
for (size_t i = 0; i < _capacity; ++i) | |
{ | |
_nodes[i] = nullptr; | |
for (auto other_node = other._nodes[i]; | |
other_node; | |
other_node = other_node->next) | |
{ | |
auto node = new Node(other_node->key, | |
other_node->value, | |
_nodes[i]); | |
_nodes[i] = node; | |
} | |
} | |
} | |
Dictionary(Dictionary&& other) noexcept | |
: Dictionary() | |
{ | |
swap(other); | |
} | |
void swap(Dictionary& other) noexcept | |
{ | |
// Enable ADL | |
using std::swap; | |
swap(_nodes, other._nodes); | |
swap(_size, other._size); | |
swap(_capacity, other._capacity); | |
swap(_threshold, other._threshold); | |
swap(_pre_hash, other._pre_hash); | |
} | |
friend void swap(Dictionary& first, Dictionary& second) noexcept | |
{ | |
first.swap(second); | |
} | |
~Dictionary() | |
{ | |
_clear(); | |
} | |
void insert(const Key& key, const Value& value) | |
{ | |
auto index = _hash(key); | |
for (auto node = _nodes[index]; node; node = node->next) | |
{ | |
if (node->key == key) | |
{ | |
node->value = value; | |
return; | |
} | |
} | |
auto node = new Node(key, value, _nodes[index]); | |
_nodes[index] = node; | |
if (++_size == _threshold) _resize(_capacity * 2); | |
} | |
void erase(const Key& key) | |
{ | |
auto index = _hash(key); | |
Node* previous = nullptr; | |
for (auto node = _nodes[index]; node; node = node->next) | |
{ | |
if (node->key == key) | |
{ | |
if (previous) previous->next = node->next; | |
delete node; | |
if (--_size == _threshold/4) _resize(_capacity * 2); | |
} | |
} | |
throw std::invalid_argument("No such key!"); | |
} | |
void clear() | |
{ | |
_clear(); | |
_size = 0; | |
_resize(minimum_capacity); | |
} | |
Value& get(const Key& key) | |
{ | |
auto found = _get(key); | |
if (! found) throw std::invalid_argument("No such key!"); | |
return found; | |
} | |
const Value& get(const Key& key) const | |
{ | |
auto found = _get(key); | |
if (! found) throw std::invalid_argument("No such key!"); | |
return found; | |
} | |
bool contains(const Key& key) const | |
{ | |
return static_cast<bool>(_get(key)); | |
} | |
Value& operator[](const Key& key) | |
{ | |
auto index = _hash(key); | |
for (auto node = _nodes[index]; node; node = node->next) | |
{ | |
if (node->key == key) | |
{ | |
return node->value; | |
} | |
} | |
auto node = new Node(key); | |
node->next = _nodes[index]; | |
_nodes[index] = node; | |
return node->value; | |
} | |
size_t size() const | |
{ | |
return _size; | |
} | |
bool is_empty() const | |
{ | |
return _size == 0; | |
} | |
pre_hash_t pre_hash() | |
{ | |
return _pre_hash; | |
} | |
void rehash() | |
{ | |
auto old = _nodes; | |
_nodes = new Node*[_capacity]; | |
_rehash(old, _capacity); | |
delete [] old; | |
} | |
void resize(size_t new_size) | |
{ | |
_resize(new_size); | |
} | |
private: | |
static const size_t _load_factor = 4; | |
struct Node | |
{ | |
Node(const Key& key_ = Key(), | |
const Value& value_ = Value(), | |
Node* next_ = nullptr) | |
: key(key_) | |
, value(value_) | |
, next(next_) | |
{ } | |
Key key; | |
Value value; | |
Node* next; | |
}; | |
void _clear() | |
{ | |
for (size_t i = 0; i < _capacity; ++i) | |
{ | |
for (auto node = _nodes[i]; node; ) | |
{ | |
auto next = node->next; | |
delete node; | |
node = next; | |
} | |
} | |
} | |
Node* _get(const Key& key) const | |
{ | |
auto index = _hash(key); | |
for (auto node = _nodes[index]; node; node = node->next) | |
{ | |
if (node->key == key) | |
{ | |
return node; | |
} | |
} | |
return nullptr; | |
} | |
size_t _hash(const Key& key) const | |
{ | |
return _pre_hash(key) % _capacity; | |
} | |
void _resize(size_t new_capacity) | |
{ | |
if (new_capacity < minimum_capacity) return; | |
size_t old_capacity = new_capacity; | |
_capacity = new_capacity; | |
_threshold = _capacity * _load_factor; | |
auto old = _nodes; | |
_nodes = new Node*[_capacity]; | |
_rehash(old, old_capacity); | |
delete [] old; | |
} | |
void _rehash(Node** old, size_t old_capacity) | |
{ | |
std::fill(_nodes, _nodes + _capacity, nullptr); | |
for (size_t i = 0; i < old_capacity; ++i) | |
{ | |
for (auto node = old[i]; node; ) | |
{ | |
auto next = node->next; | |
auto index = _hash(node->key); | |
node->next = _nodes[index]; | |
_nodes[index] = node; | |
node = next; | |
} | |
} | |
} | |
pre_hash_t _pre_hash; | |
Node** _nodes; | |
size_t _size; | |
size_t _capacity; | |
size_t _threshold; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment