从零开始手写STL库–Map的实现
Github链接:miniSTL
一、Map是什么
std::map是基于红黑树构建的数组结构,能够储存键和值这样的数据对,并且不允许重复元素的存在
二、Set要包含什么函数
基于本流程中实现过的红黑树,封装一层就可以了
不过这里额外实现一下std::map的访问方式,也就是at
和operator[]
的实现
正常的封装一下插入删除查找等函数:
template<typename Key, typename Value>
class myMap
{
private:
myRedBlackTree<Key, Value> rbTree;
public:
Map() : rbTree() {}
~Map() {}
void insert(const Key &key, const Value &value) { rbTree.insert(key, value); }
void erase(const Key &key) { rbTree.remove(key); }
size_t size() { return rbTree.getSize(); }
bool empty() const { return rbTree.empty(); }
bool contains(const Key &key) { return rbTree.at(key) != nullptr; }
};
关于at
的实现则调用红黑树的查找函数,如下:
Value &at(const Key &key)
{
Value *foundVal = rbTree.at(key);
if (foundVal)
{
return *foundVal;
}
else
{
throw std::out_of_range("Key not found");
}
}
同样的,operator[]
的重构也调用at函数,如下:
Value &operator[](const Key &key)
{
Value *foundVal = rbTree.at(key);
if (foundVal) return *foundVal;
else
{
Value defaultValue;
rbTree.insert(key, defaultValue);
return *rbTree.at(key);
}
}
不同的在于,如果operator[]
访问发现没有这个元素,会将该元素插入进树中
这里也是符合STL库的使用规范的,因为在STL库中,虽然at和[]都可以访问元素,但是原理是不同的
在vector中:
at()访问会做边界检查,如果越界会抛出异常,相对来说安全
operator[]不会,即便是越界也会返回一个引用,只是这个引用必然是错误的,基于该返回值做什么操作都有些危险
在map中:
operator[]会检查元素是否存在,如果不存在就插入该元素,并返回引用
所以这里的实现就将这一过程复现了,关于operator[]
的知识点,在Effective STL的第二十四条中也有介绍:Effective STL
有关map的插入效率问题,可以串联起来看
总结
map的查找删除搜索效率一样,都是O(logn),这是由于它是由红黑树为底层构建的
还需要注意一个问题:如果std::map的键类型是自定义类型,需要怎么做?
答案是重载operator<
或者定义比较函数,不过根据Effective STL的意见,更合适的方式是定义比较函数
不过两者均可,不考虑别的程序员可能误解代码的情况下,使用哪个方法都可以,如:
struct myCompare
{
bool operator()(const myKey& a, const myKey& b) const
{
return a.key < b.key;
}
};
std::map<myKey, int, myCompare> myMap;
或者:
struct myKey
{
int key;
bool operator<(const myKey& other) const
{
return key < other.key;
}
};
std::map<myKey, int> myMap;