当前位置: 首页 > news >正文

哈希封装“unordered_set·map“

本文与对set·map的封装高度相似,可以参考我之前的对set·map封装的文章:

链接:(没看过的话就点点我吧😚😚😚😚😚😚😚😚😚)icon-default.png?t=O83Ahttps://blog.csdn.net/Small_entreprene/article/details/143026089?fromshare=blogdetail&sharetype=blogdetail&sharerId=143026089&sharerefer=PC&sharesource=Small_entreprene&sharefrom=from_link正片开始:

源码及框架分析

由于源码整体来说比较复杂,我们截取框架部分:

// stl_hash_set
template <class Value, class HashFcn = hash<Value>,class EqualKey = equal_to<Value>,class Alloc = alloc>
class hash_set
{
private:typedef hashtable<Value, Value, HashFcn, identity<Value>,EqualKey, Alloc> ht;ht rep;
public:typedef typename ht::key_type key_type;typedef typename ht::value_type value_type;typedef typename ht::hasher hasher;typedef typename ht::key_equal key_equal;typedef typename ht::const_iterator iterator;typedef typename ht::const_iterator const_iterator;hasher hash_funct() const { return rep.hash_funct(); }key_equal key_eq() const { return rep.key_eq(); }
};
// stl_hash_map
template <class Key, class T, class HashFcn = hash<Key>,class EqualKey = equal_to<Key>,class Alloc = alloc>
class hash_map
{
private:typedef hashtable<pair<const Key, T>, Key, HashFcn,select1st<pair<const Key, T> >, EqualKey, Alloc> ht;ht rep;
public:typedef typename ht::key_type key_type;typedef T data_type;typedef T mapped_type;typedef typename ht::value_type value_type;typedef typename ht::hasher hasher;typedef typename ht::key_equal key_equal;typedef typename ht::iterator iterator;typedef typename ht::const_iterator const_iterator;
};
// stl_hashtable.h
template <class Value, class Key, class HashFcn,class ExtractKey, class EqualKey,class Alloc>
class hashtable {
public:typedef Key key_type;typedef Value value_type;typedef HashFcn hasher;typedef EqualKey key_equal;
private:hasher hash;key_equal equals;ExtractKey get_key;typedef __hashtable_node<Value> node;vector<node*, Alloc> buckets;size_type num_elements;
public:typedef __hashtable_iterator<Value, Key, HashFcn, ExtractKey, EqualKey,Alloc> iterator;pair<iterator, bool> insert_unique(const value_type& obj);const_iterator find(const key_type& key) const;
};
template <class Value>
struct __hashtable_node
{__hashtable_node* next;Value val;
};

  • 通过源码可以看到,结构上hash_map和hash_set跟map和set的完全类似,复⽤同⼀个hashtable实现key和key/value结构,hash_set传给hash_table的是两个key,hash_map传给hash_table的是pair<const key, value>;
  • 需要注意的是源码⾥⾯跟map/set源码类似,命名⻛格⽐较乱,这⾥⽐map和set还乱,hash_set模板参数居然⽤的Value命名,hash_map⽤的是Key和T命名,可⻅⼤佬有时写代码也不规范,乱弹琴。下⾯我们模拟⼀份⾃⼰的出来,就按⾃⼰的⻛格⾛了。

模拟实现unordered_set·map 

复用哈希表的框架的实现

复用的哈希表泛型化:

  • 参考源码框架,unordered_map和unordered_set复⽤之前我们实现的哈希表;
  • 我们这⾥相⽐源码调整⼀下,key参数就⽤K,value参数就⽤V,哈希表中的数据类型,我们使⽤T;
  • 其次跟map和set相⽐⽽⾔unordered_map和unordered_set的模拟实现类结构更复杂⼀点,但是⼤框架和思路是完全类似的。因为HashTable实现了泛型不知道T参数导致是K,还是pair<K, V>,那么insert内部进⾏插⼊时要⽤K对象转换成整形取模和K⽐较相等,因为pair的value不参与计算取模,且默认⽀持的是key和value⼀起⽐较相等,我们需要时的任何时候只需要⽐较K对象,所以我们在unordered_map和unordered_set层分别实现⼀个MapKeyOfT和SetKeyOfT的仿函数传给HashTable的KeyOfT,然后HashTable中通过KeyOfT仿函数取出T类型对象中的K对象,再转换成整形取模和K⽐较相等,具体细节参考如下代码实现:

框架(仿函数:这里实现XXXKeyofT)(后面会增添HashImitFunc仿函数等接口)

unordered_set:

template<class K>
class unordered_set
{struct SetKeyOfT{const K& operator()(const K& key){return key;}};
public:bool insert(const K& key){return _ht.Insert(key);}
private:hash_bucket::HashTable<K, K, SetKeyOfT> _ht;
};

unordered_map:

template<class K, class V>
class unordered_map
{struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};
public:bool insert(const pair<K, V>& kv){return _ht.Insert(kv);}
private:hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT> _ht;
};

支持iterator的实现

iterator核⼼源代码
template <class Value, class Key, class HashFcn,class ExtractKey, class EqualKey, class Alloc>
struct __hashtable_iterator {typedef hashtable<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc>hashtable;typedef __hashtable_iterator<Value, Key, HashFcn,ExtractKey, EqualKey, Alloc>iterator;typedef __hashtable_const_iterator<Value, Key, HashFcn,ExtractKey, EqualKey, Alloc>const_iterator;typedef __hashtable_node<Value> node;typedef forward_iterator_tag iterator_category;typedef Value value_type;node* cur;hashtable* ht;__hashtable_iterator(node* n, hashtable* tab) : cur(n), ht(tab) {}__hashtable_iterator() {}reference operator*() const { return cur->val; }
#ifndef __SGI_STL_NO_ARROW_OPERATORpointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */iterator& operator++();iterator operator++(int);bool operator==(const iterator& it) const { return cur == it.cur; }bool operator!=(const iterator& it) const { return cur != it.cur; }
};
template <class V, class K, class HF, class ExK, class EqK, class A>
__hashtable_iterator<V, K, HF, ExK, EqK, A>&
__hashtable_iterator<V, K, HF, ExK, EqK, A>::operator++()
{const node* old = cur;cur = cur->next;if (!cur) {size_type bucket = ht->bkt_num(old->val);while (!cur && ++bucket < ht->buckets.size())cur = ht->buckets[bucket];}return *this;
}

iterator实现思路分析

  • iterator实现的⼤框架跟list的iterator思路是⼀致的,⽤⼀个类型封装结点的指针,再通过重载运算符实现,迭代器像指针⼀样访问的⾏为,要注意的是哈希表的迭代器是单向迭代器;
  • 这⾥的难点是operator++的实现。iterator中有⼀个指向结点的指针,如果当前桶下⾯还有结点,则结点的指针指向下⼀个结点即可。如果当前桶⾛完了,则需要想办法计算找到下⼀个桶。这⾥的难点是反⽽是结构设计的问题,参考上⾯的源码,我们可以看到iterator中除了有结点的指针,还有哈希表对象的指针,这样当前桶⾛完了,要计算下⼀个桶就相对容易多了,⽤key值计算出当前桶位置,依次往后找下⼀个不为空的桶即可;
  • begin()返回第⼀个桶中第⼀个节点指针构造的迭代器,这⾥end()返回迭代器可以⽤空表⽰;
  • unordered_set的iterator也不⽀持修改,我们把unordered_set的第⼆个模板参数改成const K即可,HashTable<K, const K, SetKeyOfT, Hash> _ht;
  • unordered_map的iterator不⽀持修改key但是可以修改value,我们把unordered_map的第⼆个模板参数pair的第⼀个参数改成const K即可, HashTable<K, pair<const K, V>MapKeyOfT, Hash> _ht;
  • ⽀持完整的迭代器还有很多细节需要修改,具体参考下⾯题的代码:
// 前置声明:HTIterator会向前查找HashTable:因为相互依赖
template<class K, class T, class KeyOfT, class Hash>
class HashTable;//告诉:我是一个类模板,上面是我的模板参数template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
struct HTIterator
{typedef HashNode<T> Node;typedef HashTable<K, T, KeyOfT, Hash> HT;typedef HTIterator<K, T, Ref, Ptr, KeyOfT, Hash> Self;//iterator的内容Node* _node;const HT* _ht;//const权限可以缩小,为了实现const迭代器HTIterator(Node* node, const HT* ht):_node(node), _ht(ht){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& s){return _node != s._node;}Self& operator++(){if (_node->_next){// 当前桶还有数据,走到当前桶下一个节点_node = _node->_next;}else{// 当前桶走完了,找下一个不为空的桶KeyOfT kot;Hash hash;size_t hashi = hash(kot(_node->_data)) % _ht->_tables.size();//_tables为私有成员,需要友元声明++hashi;while (hashi < _ht->_tables.size()){_node = _ht->_tables[hashi];if (_node)break;else++hashi;//记得++,不然就死循环了}// 所有桶都走完了,end()给的空标识的_nodeif (hashi == _ht->_tables.size()){_node = nullptr;}}return *this;}
};

 map⽀持[]的实现

  • unordered_map要⽀持[]主要需要修改insert返回值⽀持,修改HashTable中的insert返回值为 pair<Iterator, bool> Insert(const T& data);
  • 有了insert⽀持[]实现就很简单了,具体参考下⾯代码实现
V& operator[](const K& key)
{pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));return ret.first->second;
}

整体的代码实现:

HashTable.h

#pragma once
#include<iostream>
#include<vector>
#include<string>
using namespace std;namespace open_address
{enum State{EXIST,//存在EMPTY,//停止DELETE//继续往后找,避免删除冲突之后,与其冲突的值找不到了,造成误判};template<class K, class V>struct HashData{pair<K, V> _kv;State _state = EMPTY;};//哈希仿函数template<class K>struct HashImitFunc{size_t operator()(const K& key){return (size_t)key;//对于自定义类型,string等就不行了}};//特化//偏特化template < >struct HashImitFunc < string>{// 参考:BKDR_Hash// 字符串转换成整形,可以把字符ascii码相加即可// 但是直接相加的话,类似"abcd"和"bcad"这样的字符串计算出是相同的// 这⾥我们使⽤BKDR哈希的思路,⽤上次的计算结果去乘以⼀个质数再加上下一个字符的ASCLL码值,这个质数⼀般是131等效果会⽐较好// 为了还是为了减少哈希冲突size_t operator()(const string& key){size_t hash = 0;for (auto e : key){hash *= 131;hash += e;}return hash;}};template<class K, class V, class Hash = HashImitFunc<K>>//加一个仿函数Hash,实现泛型编程,解决float等的取模问题class HashTable{public:HashTable():_tables(__stl_next_prime(0)), _n(0){}//实现更好的扩容inline unsigned long __stl_next_prime(unsigned long n){// Note: assumes long is at least 32 bits.static const int __stl_num_primes = 28;static const unsigned long __stl_prime_list[__stl_num_primes] = {53, 97, 193, 389, 769,1543, 3079, 6151, 12289, 24593,49157, 98317, 196613, 393241, 786433,1572869, 3145739, 6291469, 12582917, 25165843,50331653, 100663319, 201326611, 402653189, 805306457,1610612741, 3221225473, 4294967291};const unsigned long* first = __stl_prime_list;const unsigned long* last = __stl_prime_list + __stl_num_primes;const unsigned long* pos = lower_bound(first, last, n);return pos == last ? *(last - 1) : *pos;}//插入bool Insert(const pair<K, V>& kv){//实现不冗余if (Find(kv.first)){return false;}//负载因子>=0.7进行扩容操作//这里要注意一下整数/整数是向下取整,应该将其中一个强制类型转换成浮点数//也可以前一个数*10,0.7改成7if (_n * 10 / _tables.size() >= 7){//vector<HashData<K, V>> newtables(_tables.size() * 2);//for (auto& data : _tables)//{//	//旧表的数据映射到新表//	if (data._state == EXIST)//	{//		size_t hash0 = data.first % _tables.size();//		//......//	}//}//_tables.swap(newtables);//下面我们使用一个比较妙的扩容逻辑HashTable<K, V, Hash> newht;//sgi版本//要+1,不然有坑,会造成扩容失败newht._tables.resize(__stl_next_prime(_tables.size() + 1));//Jave版本/*++_m;newht._tables.resize(pow(2, _m));*/for (auto& data : _tables){//旧表的数据映射到新表if (data._state == EXIST){newht.Insert(data._kv);//这样线性探测,二次探测等的执行就可以一体化了,就是一种复用的思想}}_tables.swap(newht._tables);//赋值也可以,但是赋值是一种深拷贝,消耗比较高}Hash hash;size_t hash0 = hash(kv.first) % _tables.size();size_t hashi = hash0;size_t i = 1;//但是如果这个表满了,就会造成死循环,所以进行了以上的扩容while (_tables[hashi]._state == EXIST){//线性探测hashi = (hash0 + i) % _tables.size();++i;}_tables[hashi]._kv = kv;_tables[hashi]._state = EXIST;++_n;return true;}//查找HashData<K, V>* Find(const K& key){Hash hash;size_t hash0 = hash(key) % _tables.size();size_t hashi = hash0;size_t i = 1;while (_tables[hashi]._state != EMPTY){//注意细节if (_tables[hashi]._state == EXIST&& _tables[hashi]._kv.first == key){return &_tables[hashi];}//线性探测hashi = (hash0 + i) % _tables.size();++i;}return nullptr;}//删除bool Erase(const K& key){HashData<K, V>* ret = Find(key);if (ret){ret->_state = DELETE;return true;}else{return false;}}private:vector<HashData<K, V>> _tables;//其实本质就是vector<pair<K, V>> _table;  这看起来可行,但是实际上在对于删除操作后的查找操作可能会造成查找失败,需要再存一个状态标识size_t _n = 0;//记录数据个数//从这体会://为什么哈希也叫散列?//因为数据是由于哈希函数造成的分散状态的存放,vector的size()并不能真正代表数据的个数};
}namespace hash_bucket
{template<class K>struct HashImitFunc{size_t operator()(const K& key){return (size_t)key;}};template<>struct HashImitFunc<string>{size_t operator()(const string& s){// BKDRsize_t hash = 0;for (auto ch : s){hash += ch;hash *= 131;}return hash;}};template<class T>//表下挂节点struct HashNode{T _data;//可以使用双向链表,也可以使用单链表,这里我们和库里面一样使用单链表//这也是为什么unordered_map的迭代器是单向迭代器HashNode* _next;HashNode(const T& data):_data(data), _next(nullptr){}};// 前置声明:HTIterator会向前查找HashTable:因为相互依赖template<class K, class T, class KeyOfT, class Hash>class HashTable;//告诉:我是一个类模板,上面是我的模板参数template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>struct HTIterator{typedef HashNode<T> Node;typedef HashTable<K, T, KeyOfT, Hash> HT;typedef HTIterator<K, T, Ref, Ptr, KeyOfT, Hash> Self;//iterator的内容Node* _node;const HT* _ht;//const权限可以缩小,为了实现const迭代器HTIterator(Node* node, const HT* ht):_node(node), _ht(ht){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& s){return _node != s._node;}Self& operator++(){if (_node->_next){// 当前桶还有数据,走到当前桶下一个节点_node = _node->_next;}else{// 当前桶走完了,找下一个不为空的桶KeyOfT kot;Hash hash;size_t hashi = hash(kot(_node->_data)) % _ht->_tables.size();//_tables为私有成员,需要友元声明++hashi;while (hashi < _ht->_tables.size()){_node = _ht->_tables[hashi];if (_node)break;else++hashi;//记得++,不然就死循环了}// 所有桶都走完了,end()给的空标识的_nodeif (hashi == _ht->_tables.size()){_node = nullptr;}}return *this;}};template<class K, class T, class KeyOfT, class Hash>class HashTable{// 友元声明:模板的友元:要加上模板的所有参数:类模板的前置声明还是友元声明都是要加上模板的所有参数template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>friend struct HTIterator;typedef HashNode<T> Node;public:typedef HTIterator<K, T, T&, T*, KeyOfT, Hash> Iterator;typedef HTIterator<K, T, const T&, const T*, KeyOfT, Hash> ConstIterator;Iterator Begin(){if (_n == 0)//空桶没有必要遍历return End();for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];if (cur){return Iterator(cur, this);}}return End();}Iterator End(){return Iterator(nullptr, this);}ConstIterator Begin() const{if (_n == 0)return End();for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];if (cur){return ConstIterator(cur, this);}}return End();}ConstIterator End() const{return ConstIterator(nullptr, this);}HashTable():_tables(__stl_next_prime(0)), _n(0){}// 拷贝构造和赋值重载也需要~HashTable(){for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];while (cur){Node* next = cur->_next;delete cur;cur = next;}_tables[i] = nullptr;}}//实现更好的扩容inline unsigned long __stl_next_prime(unsigned long n){// Note: assumes long is at least 32 bits.static const int __stl_num_primes = 28;static const unsigned long __stl_prime_list[__stl_num_primes] = {53, 97, 193, 389, 769,1543, 3079, 6151, 12289, 24593,49157, 98317, 196613, 393241, 786433,1572869, 3145739, 6291469, 12582917, 25165843,50331653, 100663319, 201326611, 402653189, 805306457,1610612741, 3221225473, 4294967291};const unsigned long* first = __stl_prime_list;const unsigned long* last = __stl_prime_list + __stl_num_primes;const unsigned long* pos = lower_bound(first, last, n);return pos == last ? *(last - 1) : *pos;}//插入pair<Iterator, bool> Insert(const T& data){KeyOfT kot;Iterator it = Find(kot(data));if (it != End())return make_pair(it, false);Hash hs;size_t hashi = hs(kot(data)) % _tables.size();// 负载因⼦==1扩容if (_n == _tables.size()){vector<Node*>newtables(__stl_next_prime(_tables.size()), nullptr);for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];while (cur){Node* next = cur->_next;// 旧表中节点,挪动新表重新映射的位置size_t hashi = hs(kot(cur->_data)) %newtables.size();// 头插到新表cur->_next = newtables[hashi];newtables[hashi] = cur;cur = next;}_tables[i] = nullptr;}_tables.swap(newtables);}// 头插Node* newnode = new Node(data);newnode->_next = _tables[hashi];_tables[hashi] = newnode;++_n;return make_pair(Iterator(newnode, this), true);}//查找Iterator Find(const K& key){KeyOfT kot;Hash hash;size_t hashi = hash(key) % _tables.size();Node* cur = _tables[hashi];while (cur){if (kot(cur->_data) == key){return Iterator(cur, this);}cur = cur->_next;}return End();}//删除bool Erase(const K& key){KeyOfT kot;size_t hashi = key % _tables.size();Node* prev = nullptr;Node* cur = _tables[hashi];while (cur){if (kot(cur->_data) == key){if (prev == nullptr){// 头结点_tables[hashi] = cur->_next;}else{// 中间节点prev->_next = cur->_next;}delete cur;--_n;return true;}else{prev = cur;cur = cur->_next;}}return false;}private:vector<Node*> _tables;//指针数组//vector<list<pair<K,V>>> _tables//其实也可以这样,但是实现对应的迭代器就很复杂size_t _n = 0;//表中存储数据得个数 };
}

Unordered_set.h

#pragma once
#include"HashTable.h"
using namespace hash_bucket;
namespace rose
{template<class K, class Hash = HashImitFunc<K>>class unordered_set{struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT,Hash>::Iterator iterator;typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT,Hash>::ConstIterator const_iterator;iterator begin(){return _ht.Begin();}iterator end(){return _ht.End();}const_iterator begin() const{return _ht.Begin();}const_iterator end() const{return _ht.End();}pair<iterator, bool> insert(const K & key){return _ht.Insert(key);}iterator Find(const K & key){return _ht.Find(key);}bool Erase(const K & key){return _ht.Erase(key);}private:hash_bucket::HashTable<K, const K, SetKeyOfT, Hash> _ht;};
}

Unordered_map.h

#pragma once
#include"HashTable.h"
using namespace hash_bucket;
namespace rose
{template<class K, class V, class Hash = HashImitFunc<K>>class unordered_map{struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};public:typedef typename hash_bucket::HashTable<K, pair<const K, V>,MapKeyOfT, Hash>::Iterator iterator;typedef typename hash_bucket::HashTable<K, pair<const K, V>,MapKeyOfT, Hash>::ConstIterator const_iterator;iterator begin(){return _ht.Begin();}iterator end(){return _ht.End();}const_iterator begin() const{return _ht.Begin();}const_iterator end() const{return _ht.End();}pair<iterator, bool> insert(const pair<K, V>& kv){return _ht.Insert(kv);}V& operator[](const K& key){pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));return ret.first->second;}iterator Find(const K& key){return _ht.Find(key);}bool Erase(const K& key){return _ht.Erase(key);}private:hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash> _ht;};
}

测试用例

#include<iostream>
#include"Unordered_Set.h"
#include"Unordered_Map.h"using namespace std;
void test_set()
{rose::unordered_set<int> s;int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14, 3,3,15 };for (auto e : a){s.insert(e);}for (auto e : s){cout << e << " ";}cout << endl;rose::unordered_set<int>::iterator it = s.begin();while (it != s.end()){// 不⽀持修改//*it += 1;cout << *it << " ";++it;}cout << endl;
}
void test_map()
{rose::unordered_map<string, string> dict;dict.insert({ "sort", "排序" });dict.insert({ "left", "左边" });dict.insert({ "right", "右边" });dict["left"] = "左边,剩余";dict["insert"] = "插入";dict["string"];rose::unordered_map<string, string>::iterator it = dict.begin();while (it != dict.end()){// 不能修改first,可以修改second//it->first += 'x';it->second += 'x';cout << it->first << ":" << it->second << endl;++it;}cout << endl;
}
int main()
{//test_set();test_map();return 0;
}

相关文章:

哈希封装“unordered_set·map“

本文与对setmap的封装高度相似&#xff0c;可以参考我之前的对setmap封装的文章&#xff1a; 链接&#xff1a;&#xff08;没看过的话就点点我吧&#x1f61a;&#x1f61a;&#x1f61a;&#x1f61a;&#x1f61a;&#x1f61a;&#x1f61a;&#x1f61a;&#x1f61a;&am…...

Bi-LSTM-CRF实现中文命名实体识别工具(TensorFlow)

项目源码获取方式见文章末尾&#xff01; 回复暗号&#xff1a;13&#xff0c;免费获取600多个深度学习项目资料&#xff0c;快来加入社群一起学习吧。 **《------往期经典推荐------》**项目名称 1.【MobileNetV2实现实时口罩检测tensorflow】 2.【卫星图像道路检测DeepLabV3P…...

从JDK 17 到 JDK 21:Java 新特性

JDK17 密封类 概念&#xff1a;密封类允许开发者控制哪些类可以继承或实现特定的类或接口。通过这种方式&#xff0c;密封类为类的继承提供了更高的安全性和可维护性。 定义&#xff1a;使用sealed代表该类为密封类&#xff0c;并用permits限制哪些类可以继承。 public sea…...

【计算机网络 - 基础问题】每日 3 题(五十七)

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?typeblog &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞…...

第十二章 章节练习created的应用

目录 一、引言 二、运行效果图 ​三、完整代码 一、引言 构建一个新闻的页面&#xff0c;页面在响应式数据准备好之后&#xff08;即created&#xff09;&#xff0c;就向后台接口请求获取新闻数据列表&#xff0c;然后赋值给Vue实例中的list列表&#xff0c;这个请求逻辑我…...

Unity 游戏性能优化实践:内存管理与帧率提升技巧

1. 引言 随着移动设备性能的逐步提升&#xff0c;游戏玩家对画质和流畅度的要求越来越高。优化 Unity 游戏性能不仅可以提升用户体验&#xff0c;还能降低设备的功耗&#xff0c;延长电池寿命。这篇文章将深入探讨如何在 Unity 中优化游戏的内存管理与帧率&#xff0c;通过多方…...

C++游戏开发详解

C 是一种广泛使用的编程语言&#xff0c;尤其在游戏开发领域有着不可替代的地位。它提供了对底层硬件的直接访问能力&#xff0c;允许开发者优化性能&#xff0c;这对于追求高帧率和低延迟的游戏来说至关重要。本文将详细介绍使用 C 进行游戏开发的基础知识和技术要点&#xff…...

三、大模型(LLMs)微调面

本文精心汇总了多家顶尖互联网公司在大模型基础知识考核中的核心考点&#xff0c;并针对这些考点提供了详尽的解答。并提供电子版本&#xff0c;见于文末百度云盘链接中&#xff0c;供读者查阅。 一、大模型微调 • 1 如果想要在某个模型基础上做全参数微调&#xff0c;究竟需要…...

Flutter升级与降级

升级 版本升级 // 升级到指定版本flutter upgrade 版本号// 升级到最新版本flutter upgrade 降级 1.需要先确定想要降级的版本号。 2.切换到系统安装Flutter的目录 3.在https://github.com/flutter/flutter&#xff0c;找到要回退的版本号对应的commit序号&#xff08;具…...

分布式并发场景的核心问题与解决方案

文章目录 分布式并发场景的核心问题与解决方案一、核心问题分析1. 分布式事务问题2. 数据一致性问题3. 并发控制问题4. 分布式锁失效问题 二、解决方案1. 分布式事务解决方案1.1 可靠消息最终一致性方案1.2 TCC方案实现 2. 缓存一致性解决方案2.1 延迟双删策略2.2 Canal方案 3.…...

D - Many Segments 2(ABC377)

题意&#xff1a;给定n和m&#xff0c;给定n个区间li&#xff0c;ri&#xff0c;求出满足区间lr不完全包含区间liri的个数 分析&#xff1a;用优先队列对区间r进行排序&#xff0c;i表示左区间&#xff0c;每次找到右区间加入即可。 代码&#xff1a; #include<bits/stdc…...

数组指针和指针数组的区别

数组指针和指针数组的区别 根据我个人的理解如下&#xff1a; 数组指针&#xff1a;指向数组的指针。着重点在于最后的指针两个字。 指针数组&#xff1a; 所有元素都是指针的数组。着重点在于最后的数组两个字。 另外来看助手的回答: Kimi: 1. **数组指针&#xff08;Ar…...

【VUE点击父组件按钮,跳转到子组件】

要实现在Vue中&#xff0c;父组件通过点击按钮进入子组件的 <el-dialog> 弹窗&#xff0c;并在弹窗中嵌套 <el-table> 表格&#xff0c;可以按照以下步骤进行编写代码&#xff1a; 在父组件中&#xff0c;定义一个数据属性用于控制子组件弹窗的显示与隐藏。 data…...

Java列表排序:方法与实践

在Java编程中&#xff0c;列表排序是一个常见且重要的任务。本文将介绍Java中对列表进行排序的几种方法&#xff0c;包括使用Collections.sort()、List.sort()以及自定义排序规则。 1. 使用Collections.sort() Collections.sort()是Java提供的一个静态方法&#xff0c;用于对…...

哈希及其封装实现unordermap和set

哈希 直接定址法 哈希和之前的红黑树的区别就是&#xff0c;它是通过映射关系来找到目标的&#xff0c;可以把它想象成之前排序的计数排序&#xff0c;那其实就是哈希的一种方法&#xff0c;叫做直接定址法。 对于比较集中的数据&#xff0c;它只需要开一段区间&#xff0c;…...

在 AMD GPU 上构建解码器 Transformer 模型

Building a decoder transformer model on AMD GPU(s) — ROCm Blogs 2024年3月12日 作者 Phillip Dang. 在这篇博客中&#xff0c;我们展示了如何使用 PyTorch 2.0 和 ROCm 在单个节点上的单个和多个 AMD GPU 上运行Andrej Karpathy’s beautiful PyTorch re-implementation …...

Canvas简历编辑器-选中绘制与拖拽多选交互设计

Canvas简历编辑器-选中绘制与拖拽多选交互设计 在之前我们聊了聊如何基于Canvas与基本事件组合实现了轻量级DOM&#xff0c;并且在此基础上实现了如何进行管理事件以及多层级渲染的能力设计。那么此时我们就依然在轻量级DOM的基础上&#xff0c;关注于实现选中绘制与拖拽多选交…...

简单工厂(Simple Factory)

简单工厂&#xff08;Simple Factory&#xff09; 在创建一个对象时不向客户暴露内部细节&#xff0c;并提供一个创建对象的通用接口。 说明&#xff1a; 简单工厂把实例化的操作单独放到一个类中&#xff0c;这个类就成为简单工厂类&#xff0c;让简单工厂类来决定应该用哪…...

ffmpeg拉流分段存储到文件-笔记

通过ffmpeg可以从rtsp网络流拉取数据并存储到本地文件里&#xff0c;如下命令。做个笔记 ffmpeg -rtsp_transport tcp -i rtsp://192.168.1.168:6880/live -c copy -f segment -segment_time 60 stream_piece_%d.mp4这条 ffmpeg 命令的作用是从一个 RTSP 流中捕获视频&#xff…...

Java 实习工资大概是多少?——解读影响薪资的因素

文章目录 1. 城市因素&#xff1a;一线、二线的差距2. 公司类型&#xff1a;互联网公司、外企和传统企业的差别3. 个人能力&#xff1a;经验、技术栈的重要性4. 其他影响因素&#xff1a;学历和实习时间总结推荐阅读文章 Java 开发作为广泛应用的职业方向&#xff0c;实习工资的…...

【Linux】万字详解:Linux文件系统与软硬链接

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Linux 目录 &#x1f680; 前言 一&#xff1a; &#x1f525; 磁盘的物理结构二&#xff1a; &#x1f525; 磁盘的存储结构 三&#xff1a; &#x1f525; 磁盘的逻辑结构 四&#xff1a; &#…...

spacenavd

介绍spacenavd开源项目&#xff0c;主要是因为在斯坦福大学的UMI项目中使用了该项目。在斯坦福大学的 UMI&#xff08;Universal Manipulation Interface&#xff09;项目中&#xff0c;Spacenavd 主要用于处理 3D Space Mouse&#xff08;空间鼠标&#xff09;的输入&#xf…...

C#WPF的XAML的语法详谈和特性

WPF的XAML&#xff08;eXtensible Application Markup Language&#xff09;是一种基于XML的标记语言&#xff0c;用于在.NET框架中定义和描述用户界面。XAML提供了一种声明性的方式来构建应用程序的UI元素&#xff0c;包括窗口、控件、布局、样式、动画和数据绑定等。 XAML的…...

一篇文章讲透数据结构之二叉搜索树

前言 在前面的学习过程中&#xff0c;我们已经学习了二叉树的相关知识。在这里我们再使用C来实现一些比较难的数据结构。 这篇文章用来实现二叉搜索树。 一.二叉搜索树 1.1二叉搜索树的定义 二叉搜索树&#xff08;Binary Search Tree&#xff09;是基于二叉树的一种升级版…...

新手入门c++(8)

到时候了&#xff0c;是时候给你们讲一下其他的定义形式与格式化输入输出了。 1.长整型变量 长整型变量分为两种&#xff1a; ①long类型 在计算机编程中&#xff0c;long 类型是一个整型数据类型&#xff0c;用于存储较大的整数。它的大小和范围取决于操作系统和编译器的实…...

新手铲屎官提问,有哪几款噪音低的宠物空气净化器推荐

相信很多铲屎官都明白的的痛就是猫咪掉毛太严重&#xff0c;所以每次看到满天飞的浮毛时只想赶紧逃离&#xff0c;一点都不想清理。但是家是自己的&#xff0c;猫是自己的&#xff0c;健康也是自己的&#xff0c;不清理也得清理。 为了更有效的清理浮毛&#xff0c;我朋友特意…...

解决RabbitMQ脑裂问题

文章目录 前言一、现象二、解决办法 前言 RabbitMQ脑裂 一、现象 RabbitMQ镜像群出现脑裂现象&#xff0c;各个节点的MQ实例都“各自为政”&#xff0c;数据并不同步。 二、解决办法 # 停止mq sh rabbitmq-server stop_app # 查看mq进程是否存在 ps -ef | grep rabbitmq # …...

经纬恒润AUTOSAR成功适配芯钛科技Alioth TTA8车规级芯片

在汽车电子领域&#xff0c;功能安全扮演着守护者的角色&#xff0c;它确保了车辆在复杂多变的情况下保持稳定可靠的运行。随着汽车电子的复杂性增加&#xff0c;市场对产品功能安全的要求也日益提高。基于此背景&#xff0c;经纬恒润AUTOSAR基础软件产品INTEWORK-EAS-CP成功适…...

4、java random随机数、一维数组、二维数组

目录 Random类与随机数生成数组的概述与使用数组的内存分配与访问数组的常见问题与解决方案一维数组的遍历与操作二维数组的概述与遍历1. Random类与随机数生成 引言 在编程中,我们经常需要生成随机数,比如在游戏、模拟实验或者数据处理中。Java提供了一个非常方便的类Rand…...

C++ 魔法三钥:解锁高效编程的封装、继承与多态

快来参与讨论&#x1f4ac;&#xff0c;点赞&#x1f44d;、收藏⭐、分享&#x1f4e4;&#xff0c;共创活力社区。 目录 &#x1f4af;前言 &#x1f4af;封装 1.封装概念 2.封装格式 3.封装的原理 4.封装的作用 &#x1f4af;继承 1.继承的概念 2.继承格式 3.继承的…...

上海网站建设最好的公司/公司网站的推广方案

Toggle就是开关的意思&#xff0c;ToggleButton就是一个开关按钮组件&#xff0c;在BB10 Cascades中可以在QML中直接加入一个ToggleButton来作为开关&#xff0c;该该控件显示出来是这个样子的&#xff1a; 如果是选中状态&#xff0c;或者说是“打开”状态显示出来是这个样子的…...

大兴做网站/开源crm系统

1、找不到方法的实现unrecognized selector sent to instance 2、KVC造成的crash 3、EXC_BAD_ACCESS 4、KVO引起的崩溃 5、集合类相关崩溃 6、多线程中的崩溃 7、Socket长连接&#xff0c;进入后台没有关闭 8、Watch Dog超时造成的crash 9、后台返回NSNull导致的崩溃&a…...

wordpress 邮件认证/seo优化方式包括

玩cloudstack的人都应该玩过模板这个功能&#xff0c;这里还是比较有意思的&#xff0c;我们底层连接vcenter 创建vm采用模板 实际这里的磁盘方案&#xff0c;并不是给系统重新分配的磁盘大小而是又新挂上了一块磁盘&#xff0c;新磁盘的大小 创建完vm后可以去虚拟里面查看磁盘…...

.net网站开发实例/企业管理培训班

本文翻译自&#xff1a;Whats the difference between map() and flatMap() methods in Java 8? 在Java 8中&#xff0c; Stream.map()和Stream.flatMap()方法之间有什么区别&#xff1f; #1楼 参考&#xff1a;https://stackoom.com/question/1nxsA/Java-中的map-和flatMap-方…...

做网站大公司还是小公司/网站怎么优化

2018年洛阳十大技校排名 排名前十的学校有哪些2018-06-23 19:25:32文/王蕊中考结束后一定有很多初中生想就读技校&#xff0c;下面&#xff0c;初三网小编就为大家整理一下2018年洛阳最好的十大技校排名&#xff0c;以便大家选择合适的技校。2018洛阳十大技校排名技校排名技校名…...

广东恒力建设工程有限公司网站/西安网站设计公司

防止人为误操作MySQL数据库技巧一例&#xff08;本题来自老男孩培训内部学生问题&#xff0c;属于数据库安全技巧&#xff09;在若干年前&#xff0c;老男孩亲自遇到一个“命案”&#xff0c;老大登录数据库update一个记录&#xff0c;结果忘了加where&#xff0c;于是悲剧发生…...