Skip to content
This repository was archived by the owner on May 5, 2025. It is now read-only.
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
307 changes: 307 additions & 0 deletions hashmap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
#include <forward_list>
#include <functional>
#include <vector>
#include <stdexcept>

template<class KeyType, class ValueType, class Hash = std::hash<KeyType>>
class HashMap {
private:
static const size_t sizes[];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

constexpr, и можно будет объявить прямо здесь.


size_t cur_size = 0, cur_len = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Непонятно, чем size отличается от len. Возможно, имелись в виду size и capacity?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Непонятно, чем size отличается от len. Возможно, имелись в виду size и capacity?

std::vector<std::forward_list<std::pair<const KeyType, ValueType>>> buckets;
Hash hasher;

void change_size(size_t new_sz) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new_size, не надо сокращать на ровном месте.

if (new_sz == cur_len) {
return;
}
new_sz = sizes[cur_len = new_sz];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

То, что язык позволяет что-то сделать, не повод так реально писать :)

std::vector<std::forward_list<std::pair<const KeyType, ValueType>>> new_buckets(new_sz);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Напрашивается using Bucket = std::forward_list<...> в начале класса.

for (auto &b : buckets) {
for (auto &x : b) {
new_buckets[hasher(x.first) % new_sz].push_front(std::move(x));
}
}
buckets.swap(new_buckets);
}

size_t load_factor() const {
return cur_size * 100 / sizes[cur_len];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Magic number.

}

bool trySizeUp() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Зачем этот метод публичный?

if (load_factor() > 130) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Magic number. Стоит сделать константу в начале класса (что-то вроде GrowLoadFactorThreshold/ShrinkLoadFactorThreshold).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Magic number. Стоит сделать константу в начале класса (что-то вроде GrowLoadFactorThreshold/ShrinkLoadFactorThreshold).

change_size(cur_len + 1);
return true;
}
return false;
}

bool trySizeDown() {
if (load_factor() < 30 && cur_len) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

&& cur_len > 0

change_size(cur_len - 1);
return true;
}
return false;
}

public:
HashMap(const Hash &Hasher = Hash()) : buckets(sizes[0]), hasher(Hasher) {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

explicit


template<class Iter>
HashMap(Iter beg, Iter end, const Hash &Hasher = Hash()) : HashMap(Hasher) {
for (Iter it = beg; it != end; ++it) {
insert(*it);
}
}

HashMap(const std::initializer_list<std::pair<const KeyType, ValueType>> &l, const Hash &Hasher = Hash()) : HashMap(l.begin(), l.end(), Hasher) {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Слишком длинная строка.


HashMap(const std::vector<std::pair<const KeyType, ValueType>> &vals, const Hash &Hasher = Hash()) : HashMap(vals.begin(), vals.end(), Hasher) {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

И эта.


size_t size() const {
return cur_size;
}

bool empty() const {
return cur_size == 0;
}

const Hash& hash_function() const {
return hasher;
}

size_t count(const KeyType &key) const {
size_t h = hasher(key) % buckets.size();
for (auto &[x, y] : buckets[h]) {
if (x == key) {
return 1;
}
}
return 0;
}

void insert(const std::pair<KeyType, ValueType> &val) {
if (count(val.first))
return;
buckets[hasher(val.first) % buckets.size()].push_front(val);
++cur_size;
trySizeUp();
}

void erase(const KeyType &key) {
size_t h = hasher(key) % buckets.size();
for (auto it = buckets[h].before_begin(); next(it) != buckets[h].end(); ++it) {
if (next(it)->first == key) {
buckets[h].erase_after(it);
--cur_size;
break;
}
}
trySizeDown();
}

ValueType& operator[](const KeyType &key) {
size_t h = hasher(key) % sizes[cur_len];
for (auto &[x, y] : buckets[h]) {
if (x == key) {
return y;
}
}
buckets[h].push_front({key, ValueType()});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

emplace_front

++cur_size;
if (trySizeUp()) {
return operator[](key);
}
return buckets[h].front().second;
}

const ValueType& at(const KeyType &key) const {
size_t h = hasher(key) % buckets.size();
for (auto &[x, y] : buckets[h]) {
if (x == key) {
return y;
}
}
throw std::out_of_range("No such key");
}

void clear() {
for (auto &b : buckets) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

auto& bucket, сокращение не нужно.

b.clear();
}
cur_size = 0;
change_size(0);
}

class const_iterator {
friend HashMap;
private:
typename std::vector<std::forward_list<std::pair<const KeyType, ValueType>>>::const_iterator cur, end;
typename std::forward_list<std::pair<const KeyType, ValueType>>::const_iterator biter;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bucketIter (и снова хочется сделать using, про который я писал выше).


void push() {
while (cur != end && biter == cur->end()) {
++cur;
if (cur != end) {
biter = cur->begin();
}
}
}

public:
const_iterator() {}
const_iterator(const HashMap &mp) : cur(mp.buckets.begin()), end(mp.buckets.end()), biter(cur->begin()) {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const hashMap& hashMap

const_iterator(const const_iterator &it) : cur(it.cur), end(it.end), biter(it.biter) {}

const_iterator& operator++() {
++biter;
push();
return *this;
}

const_iterator operator++(int) {
const_iterator cp(*this);
++(*this);
return cp;
}

const_iterator& operator=(const const_iterator &iter) {
cur = iter.cur;
end = iter.end;
biter = iter.biter;
return *this;
}

const std::pair<const KeyType, ValueType>& operator*() {
return *biter;
}

typename std::forward_list<std::pair<const KeyType, ValueType>>::const_iterator operator->() {
return biter;
}

bool operator==(const const_iterator &it) {
return it.cur == cur && it.biter == biter;
}

bool operator!=(const const_iterator &it) {
return !(*this == it);
}
};

class iterator {
friend HashMap;
private:
typename std::vector<std::forward_list<std::pair<const KeyType, ValueType>>>::iterator cur, end;
typename std::forward_list<std::pair<const KeyType, ValueType>>::iterator biter;

void push() {
while (cur != end && biter == cur->end()) {
++cur;
if (cur != end) {
biter = cur->begin();
}
}
}

public:
iterator() {}
iterator(HashMap &mp) : cur(mp.buckets.begin()), end(mp.buckets.end()), biter(cur->begin()) {}
iterator(const iterator &it) : cur(it.cur), end(it.end), biter(it.biter) {}

iterator& operator++() {
++biter;
push();
return *this;
}

iterator operator++(int) {
iterator cp(*this);
++(*this);
return cp;
}

iterator& operator=(const iterator &iter) {
cur = iter.cur;
end = iter.end;
biter = iter.biter;
return *this;
}

const std::pair<const KeyType, ValueType>& operator*() {
return *biter;
}

typename std::forward_list<std::pair<const KeyType, ValueType>>::iterator operator->() {
return biter;
}

bool operator==(const iterator &it) {
return it.cur == cur && it.biter == biter;
}

bool operator!=(const iterator &it) {
return !(*this == it);
}
};

const_iterator find(const KeyType &val) const {
size_t h = hasher(val) % buckets.size();
for (auto it = buckets[h].begin(); it != buckets[h].end(); ++it) {
if (it->first == val) {
const_iterator iter(*this);
iter.cur = buckets.begin() + h;
iter.biter = it;
return iter;
}
}
return end();
}

iterator find(const KeyType &val) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Давай попробуем избавиться от такого дублирования между find и find count.

size_t h = hasher(val) % buckets.size();
for (auto it = buckets[h].begin(); it != buckets[h].end(); ++it) {
if (it->first == val) {
iterator iter(*this);
iter.cur = buckets.begin() + h;
iter.biter = it;
return iter;
}
}
return end();
}

iterator begin() {
iterator iter(*this);
iter.push();
return iter;
}

iterator end() {
iterator iter(*this);
iter.cur = iter.end;
iter.biter = buckets.back().end();
return iter;
}

const_iterator begin() const {
const_iterator iter(*this);
iter.push();
return iter;
}

const_iterator end() const {
const_iterator iter(*this);
iter.cur = iter.end;
iter.biter = buckets.back().end();
return iter;
}
};

template<class KeyType, class ValueType, class Hash>
const size_t HashMap<KeyType, ValueType, Hash>::sizes[] = {
5, 11, 23, 47, 97, 197, 397, 797, 1597, 3203, 6421,
12853, 25717, 51437, 102877, 205759, 411527, 823117,
1646237, 3292489, 6584983, 13169977, 26339969, 52679969};