unordered_map和unordered_set

目录

一、unordered_map

1.1、unordered_map的特点

1.2、unordered_map和map的区别

二、unordered_set

2.1、unordered_set的特点

2.2、unordered_set和set的区别

三、哈系桶的改造

3.1 结构设置

3.2 构造函数和析构函数

3.3 数据插入

3.4 数据查找

3.5 数据删除

3.6 迭代器实现

3.7、友元声明

​编辑

四、unordered_map封装

五、unordered_set封装

六、哈希表


一、unordered_map

unordered_map其实就是与map相对应的一个容器。学过map就知道,map的底层是一个红黑树,通过中序遍历的方式可以以有序的方式遍历整棵树。而unordered_map,正如它的名字一样,它的数据存储其实是无序的,这也和它底层所使用的哈希结构有关。而在其他功能上,unordered_map和map基本上就是一致的。

1.1、unordered_map的特点

(1)unordered_map是用于存储<key, value>键值对的关联式容器,它允许通过key快速的索引到对应的value。

(2)在内部,unorder_map没有对<key, value>按照任何特定的顺序排序,为了能在常数范围内找到key所对应的value,unordered_map将相同哈希值的<key, value>键值对放在相同的桶中。

(3)unordered_map容器的搜索效率比map快,但它在遍历元素自己的范围迭代方面效率就比较低。

(4)它的迭代器只能向前迭代,不支持反向迭代。

1.2、unordered_map和map的区别

从大结构上看,unordered_map和map的模板其实没有太大差距。学习了map和set我们就应该知道,map是通过T来告诉红黑树要构造的树的存储数据类型的,unordered_map也是一样的,但是它的参数中多了Hash和Pred两个参数,这两个参数都传了仿函数,主要和哈希结构有关。这里先不过多讲解

二、unordered_set

unordered_set其实也是一样的,从功能上来看和set并没有什么区别,只是由于地层数据结构的不同,导致unordered_set的数据是无序的,但是查找效率非常高。

2.1、unordered_set的特点

  1. 无序性:unordered_set中的元素没有特定的顺序,不会根据插入的顺序或者元素的值进行排序。

  2. 唯一性:unordered_set中的元素是唯一的,不允许重复的元素。

  3. 快速查找:unordered_set使用哈希表实现,可以在平均常数时间内进行查找操作,即使在大型数据集上也能保持高效。

  4. 插入和删除效率高:unordered_set的插入和删除操作的平均时间复杂度为常数时间,即O(1)。

  5. 高效的空间利用:unordered_set使用哈希表来存储元素,不会浪费额外的空间。

  6. 不支持修改操作:由于unordered_set中的元素是唯一的,不支持直接修改元素的值,需要先删除旧值,再插入新值。

  7. 迭代器失效:在进行插入和删除

2.2、unordered_set和set的区别

很明显,unordered_set相较于set,多了Hash和Pred两个参数。这两个参数都是传了仿函数,和unordered_map与map之间的关系都是一样的,这里先不过多讲解。

三、哈系桶的改造

3.1 结构设置

首先,这里实现的哈希桶需要能够同时支持unordered_map和unordered_set的调用。因此,在传入的数据中就需要有一个T,来标识传入的数据类型。同时,还需要有Hash函数和KeyOfT来分别对传入的数据转换为整形和获取传入数据的key值,主要是提供给使用了KV模型的数据。

我们还要知道,哈希桶其实是保存在一个顺序表中的,每个下标对应的位置上都是桶的头节点,每个桶中的数据以单链表的方式链接起来。因此,我们就需要一个vector来存储结构体指针,这个结构体中包含了当前节点存储的数据和下一个节点的位置。当然,还有有一个_n来记录顺序表中数据的个数。

namespace BucketHash//哈希桶
{//哈希桶内的每个节点/template<class T>struct HashNode{T _data;//存储数据HashNode<T>* _next;//指向下一个节点HashNode(const T& data): _data(data), _next(nullptr){}};template<class K, class T, class Hash, class KeyOfT>class HashBucket{template<class K, class T, class Hash, class KeyOfT>friend struct HashIterator;//友元声明,让迭代器可以访问哈希桶的私有成员typedef HashNode<T> Node;public:typedef HashIterator<K, T, Hash, KeyOfT> iterator;private:vector<Node*> _bucket;//存储数据的指针数组size_t _n;};
}

在类的模板参数中,Hash为将数据转化为整形的仿函数KeyOfT是返回键值的仿函数。

注意,上面的代码中有一个迭代器的重命名和迭代器结构体的友元声明。重命名是为了方便后续的使用。友元声明则和迭代器的实现有关,这里先不过多讲解。

3.2 构造函数和析构函数

注意,这里没有实现拷贝构造。并不是不需要实现,实际上,虽然哈希桶内的成员是自定义类型的,会去调自己的拷贝构造,但是这里只会进行浅拷贝,不满足需要。如果有需要,可以自己实现拷贝构造,实现起来也很简单。

HashBucket():_n(0)
{_bucket.resize(10);//构建时默认开10个空间
}~HashBucket()//析构函数
{for (auto& cur : _bucket){while (cur){Node* prev = cur;cur = cur->_next;delete prev;prev = nullptr;}}
}

3.3 数据插入

insert()函数返回的是pair<iterator, bool>,这是为了后续实现 [ ] 重载做准备。

在插入时,首先要先查看哈希桶中是否存在相同键值,存在就直接返回当前位置。第二步就是要查看哈希桶中的元素个数与哈希桶的容量之间的负载因子,如果等于1,就需要进行扩容。第三步则是开始插入节点。先找到映射位置,然后新建一个节点连接到对应的数组下标的空间中即可

pair<iterator, bool> insert(const T& data)//插入数据
{KeyOfT kt;iterator it = find(kt(data));if (it != end())//不允许数据重复,找到相同的返回return make_pair(it, false);if (_bucket.size() == _n)//负载因子设置为1,超过就扩容{vector<Node*> newbucket;//这种方式就无需再开空间拷贝节点newbucket.resize(NextPrime(_bucket.size()));//开一个素数大小的空间for (size_t i = 0; i < _bucket.size(); ++i){Node* cur = _bucket[i];while (cur){Node* next = cur->_next;size_t hashi = Hash()(kt(cur->_data)) % newbucket.size();//找映射位置cur->_next = newbucket[hashi];//头插到新哈希表newbucket[hashi] = cur;cur = next;}_bucket[i] = nullptr;//将原哈希表中的指针置为空}_bucket.swap(newbucket);//将newbucket中的节点全部交换给_bucket}Hash knt;size_t hashi = knt(kt(data)) % _bucket.size();//找映射位置Node* newnode = new Node(data);newnode->_next = _bucket[hashi];//头插,让插入的节点的下一个指针指向头节点_bucket[hashi] = newnode;//让头节点指向插入的节点++_n;return make_pair(iterator(newnode, this), true);
}

3.4 数据查找

查找数据很简单。先通过hash函数计算出要查找的数据键值所对应的位置,如果对应的位置上存储的是空,说明不存在,直接返回。如果不为空,则比对键值,相同返回,不相同向下找直到为空。

iterator find(const K& key)//查找
{size_t pos = Hash()(key) % _bucket.size();//找映射位置	Node* cur = _bucket[pos];while (cur){if (KeyOfT()(cur->_data) == key)return iterator(cur, this);elsecur = cur->_next;}return end();
}

3.5 数据删除

哈希桶与哈希表不同,要删除数据时不能使用find()函数搜索。因为哈希桶中的数据是用单链表链接起来的,用find()就无法知道节点的父节点,进而无法链接链表。

因此,在删除时,首先要调用hash函数获取关键码,根据关键码蛆对应位置上找。如果该位置存的数据为空,则表示不存在,返回;如果不为空,就比较键值,相同为找到,删除,不同就继续向下找直到为空。

bool erase(const K& key)//删除
{size_t hashi = Hash()(key) % _bucket.size();//找映射位置Node* cur = _bucket[hashi];Node* prev = nullptr;while (cur){if (KeyOfT()(cur->_data) == key){if (cur == _bucket[hashi])_bucket[hashi] = cur->_next;elseprev->_next = cur->_next;delete cur;--_n;cur = nullptr;return true;}else{prev = cur;cur = cur->_next;}}return false;
}

3.6 迭代器实现

哈希桶中的迭代器实现方式比较复杂。要使用迭代器,首先就要有能够遍历哈希桶的手段。因此,为了便于遍历,迭代器的结构体中首先要有哈希桶的指针。当然,迭代器的结构体中还需要有一个数据的指针,用于初始化迭代器,获取对应位置上的内容。

要遍历哈希桶很简单,和find()的逻辑差不多。直接判断当前节点是否为空,不为空则返回;为空就说明当前下标对应的位置上已经没有数据了,就调用hash函数获取该节点的关键码。++关键码走向下一个下标,不为空则返回;为空则继续走,直到找到不为空的位置或结束。

如上图所示,一个哈希表,其中有四个哈希桶,迭代器是it。

++it操作:

  • 如果it不是某个桶的最后一个元素,则it指向下一个节点。
  • 如果it是桶的最后一个元素,则it指向下一个桶的头节点。
template<class K, class T, class Hash, class KeyOfT>
class HashBucket;//前置声明,因为迭代器中使用了HashBucket的模板,但是迭代器在它之前无法找到,所以要前置声明template<class K, class T, class Hash, class KeyOfT>
struct HashIterator
{typedef HashNode<T> Node;typedef HashIterator<K, T, Hash, KeyOfT> Self;typedef HashBucket<K, T, Hash, KeyOfT> HB;//将哈希桶传进来HashIterator(Node* node, HB* hb):_node(node),_hb(hb){}T& operator*()//解引用{return _node->_data;}T* operator->()//运算符->重载{return &_node->_data;}bool operator!=(const Self& sl) const//判断是否相等{return _node != sl._node;}Self operator++()//运算符++重载{if (_node->_next)//节点的下一个位置不为空{_node = _node->_next;}else//节点的下一个位置为空,说明该位置上已经没有值,找下一个不为空的桶节点{KeyOfT kt;size_t hashi = Hash()(kt(_node->_data)) % _hb->_bucket.size();++hashi;while (hashi < _hb->_bucket.size())//当前位置小于桶的数量{if (_hb->_bucket[hashi])//如果hashi位置上不为空,则修改_node的值{_node = _hb->_bucket[hashi];break;}else++hashi;}if (hashi == _hb->_bucket.size())//如果hashi的位置与桶的数量相当,说明没有找到_node = nullptr;}return *this;}Node* _node;//节点指针HB* _hb;//哈希桶的指针
};

注意,因为模板是向上寻找的,在迭代器的类模板中使用了哈希桶的类模板,所以如果迭代器类模板在哈希桶类模板之上,就需要进行类声明,让迭代器类模板能找到哈希桶类模板的位置。反之亦然。

注意,因为迭代器中传入了哈希桶,要对哈希桶进行遍历。但因为哈希桶中的成员变量都被设置为了私有,所以可以将迭代器声明为哈希桶的友元类,也可以单独提供一个获取哈希桶指针的函数。

有了迭代器的类模板后,哈希桶的迭代器实现起来就很轻松了。

iterator begin()
{for (size_t i = 0; i < _bucket.size(); ++i){if (_bucket[i]){return iterator(_bucket[i], this);}}return iterator(nullptr, this);
}iterator end()
{return iterator(nullptr, this);
}

注意,哈希桶的begin()要返回的是第一个不为空的桶,而不是第一个节点

3.7、友元声明

在++迭代器的时候,会使用到哈希表指针,哈希表指针又会使用到HashTable中的_tables。

  • HashTable中的_tables是私有成员,在类外是不能访问的。

解决这个问题可以在HashTable中写一个公有的访问函数,也可以采用友元,本喵这里就是使用的友元的方式。

  • 类模板的友元声明需要写模板参数,在类名前面加friend关键字,如上图绿色框中所示。

迭代器中的其他操作,如解引用,箭头,以及相等等运算符的重载本喵就不再详细介绍了,后面本喵会附源码,直接看代码即可。

四、unordered_map封装

#pragma once
#include "HashTable.h"
namespace lyl
{template<class K, class V, class Hash = HashFunc<K>>class unordered_map{public:struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};public:typedef typename HashBucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::iterator iterator;typedef typename HashBucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::const_iterator 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 = 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:HashBucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash> _ht;};}}

五、unordered_set封装

#pragma once#include "HashTable.h"namespace lyl
{template<class K, class Hash = HashFunc<K>>class unordered_set{public:struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:typedef typename HashBucket::HashTable<K, K, SetKeyOfT, Hash>::const_iterator iterator;typedef typename HashBucket::HashTable<K, K, SetKeyOfT, Hash>::const_iterator 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:HashBucket::HashTable<K, K, SetKeyOfT, Hash> _ht;};}

六、哈希表

#pragma once
#include <vector>
#include<iostream>namespace OpenAddress
{enum State{EMPTY,EXIST,DELETE};template<class K, class V>struct HashData{pair<K, V> _kv;State _state = EMPTY;};template<class K, class V>class HashTable{public:bool Insert(const pair<K, V>& kv){if (Find(kv.first))return false;// 负载因子超过0.7就扩容//if ((double)_n / (double)_tables.size() >= 0.7)if (_tables.size() == 0 || _n * 10 / _tables.size() >= 7){//size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;//vector<HashData> newtables(newsize);遍历旧表,重新映射到新表//for (auto& data : _tables)//{//	if (data._state == EXIST)//	{//		// 重新算在新表的位置//		size_t i = 1;//		size_t index = hashi;//		while (newtables[index]._state == EXIST)//		{//			index = hashi + i;//			index %= newtables.size();//			++i;//		}//		newtables[index]._kv = data._kv;//		newtables[index]._state = EXIST;//	}//}//_tables.swap(newtables);size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;HashTable<K, V> newht;newht._tables.resize(newsize);// 遍历旧表,重新映射到新表for (auto& data : _tables){if (data._state == EXIST){newht.Insert(data._kv);}}_tables.swap(newht._tables);}size_t hashi = kv.first % _tables.size();// 线性探测size_t i = 1;size_t index = hashi;while (_tables[index]._state == EXIST){index = hashi + i;index %= _tables.size();++i;}_tables[index]._kv = kv;_tables[index]._state = EXIST;_n++;return true;}HashData<K, V>* Find(const K& key){if (_tables.size() == 0){return 0;}size_t hashi = key % _tables.size();// 线性探测size_t i = 1;size_t index = hashi;while (_tables[index]._state != EMPTY){if (_tables[index]._state == EXIST&& _tables[index]._kv.first == key){return &_tables[index];}index = hashi + i;index %= _tables.size();++i;// 如果已经查找一圈,那么说明全是存在+删除if (index == hashi){break;}}return nullptr;}bool Erase(const K& key){HashData<K, V>* ret = Find(key);if (ret){ret->_state = DELETE;--_n;return true;}else{return false;}}private:vector<HashData<K, V>> _tables;size_t _n = 0; // 存储的数据个数//HashData* tables;//size_t _size;//size_t _capacity;};
}template<class K>
struct HashFunc
{size_t operator()(const K& key){return key;}
};// 特化
template<>
struct HashFunc<string>
{// BKDRsize_t operator()(const string& s){size_t hash = 0;for (auto ch : s){hash += ch;hash *= 31;}return hash;}
};namespace HashBucket
{template<class T>struct HashNode{HashNode<T>* _next;T _data;HashNode(const T& data):_next(nullptr), _data(data){}};// 前置声明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 __HashIterator{typedef HashNode<T> Node;typedef HashTable<K, T, KeyOfT, Hash> HT;typedef __HashIterator<K, T, Ref, Ptr, KeyOfT, Hash> Self;typedef __HashIterator<K, T, T&, T*, KeyOfT, Hash> Iterator;Node* _node;const HT* _ht;__HashIterator(Node* node, const HT* ht):_node(node), _ht(ht){}__HashIterator(const Iterator& it):_node(it._node), _ht(it._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 != nullptr){_node = _node->_next;}else{// 找下一个不为空的桶KeyOfT kot;Hash hash;// 算出我当前的桶位置size_t hashi = hash(kot(_node->_data)) % _ht->_tables.size();++hashi;while (hashi < _ht->_tables.size()){if (_ht->_tables[hashi]){_node = _ht->_tables[hashi];break;}else{++hashi;}}// 没有找到不为空的桶if (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 __HashIterator;typedef HashNode<T> Node;public:typedef __HashIterator<K, T, T&, T*, KeyOfT, Hash> iterator;typedef __HashIterator<K, T, const T&, const T*, KeyOfT, Hash> const_iterator;iterator begin(){Node* cur = nullptr;for (size_t i = 0; i < _tables.size(); ++i){cur = _tables[i];if (cur){break;}}return iterator(cur, this);}iterator end(){return iterator(nullptr, this);}const_iterator begin() const{Node* cur = nullptr;for (size_t i = 0; i < _tables.size(); ++i){cur = _tables[i];if (cur){break;}}return const_iterator(cur, this);}const_iterator end() const{return const_iterator(nullptr, this);}~HashTable(){for (auto& cur : _tables){while (cur){Node* next = cur->_next;delete cur;cur = next;}cur = nullptr;}}iterator Find(const K& key){if (_tables.size() == 0)return end();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){Hash hash;KeyOfT kot;size_t hashi = hash(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;return true;}else{prev = cur;cur = cur->_next;}}return false;}// 休息一下:15:55继续// size_t newsize = GetNextPrime(_tables.size());size_t GetNextPrime(size_t prime){// SGIstatic 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};size_t i = 0;for (; i < __stl_num_primes; ++i){if (__stl_prime_list[i] > prime)return __stl_prime_list[i];}return __stl_prime_list[i];}pair<iterator, bool> Insert(const T& data){KeyOfT kot;iterator it = Find(kot(data));if (it != end()){return make_pair(it, false);}Hash hash;// 负载因因子==1时扩容if (_n == _tables.size()){/*size_t newsize = _tables.size() == 0 ? 10 : _tables.size()*2;HashTable<K, V> newht;newht.resize(newsize);for (auto cur : _tables){while (cur){newht.Insert(cur->_kv);cur = cur->_next;}}_tables.swap(newht._tables);*///size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;size_t newsize = GetNextPrime(_tables.size());vector<Node*> newtables(newsize, nullptr);//for (Node*& cur : _tables)for (auto& cur : _tables){while (cur){Node* next = cur->_next;size_t hashi = hash(kot(cur->_data)) % newtables.size();// 头插到新表cur->_next = newtables[hashi];newtables[hashi] = cur;cur = next;}}_tables.swap(newtables);}size_t hashi = hash(kot(data)) % _tables.size();// 头插Node* newnode = new Node(data);newnode->_next = _tables[hashi];_tables[hashi] = newnode;++_n;return make_pair(iterator(newnode, this), false);;}size_t MaxBucketSize(){size_t max = 0;for (size_t i = 0; i < _tables.size(); ++i){auto cur = _tables[i];size_t size = 0;while (cur){++size;cur = cur->_next;}//printf("[%d]->%d\n", i, size);if (size > max){max = size;}}return max;}private:vector<Node*> _tables; // 指针数组size_t _n = 0; // 存储有效数据个数};
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/452823.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Elasticsearch(ES) 简述请求操作索引下文档 增删查改操作

上文 Elasticsearch(ES) 创建带有分词器规则的索引 带着大家创建了一个带有分词功能的索引 老规矩 我们启动一下ES服务 本文 我们就来说说 关于文档的操作 我们先来添加一个文档 就像数据库加一条数据一样 这里 并不需要指定什么表结构和数据结构 它的文档结构是无模式的 添…

【代码随想录-哈希表】两个数组的交集

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老导航 檀越剑指大厂系列:全面总结 jav…

什么是功能安全?

前言 在上一家公司的时候&#xff0c;有幸参加过公司内部的技术分享会&#xff0c;有一个同事跟我们分享了功能安全的一些内容。在提问环节&#xff0c;我问了一个问题“什么是功能安全&#xff1f;”他回答不上来。这也是我们很多人在工作中常犯的一个问题&#xff1a;我们做了…

天拓四方:边缘计算网关功能、特点与应用举例

传统的数据处理方式面临网络延迟、带宽限制和安全风险等问题。为了解决这些问题&#xff0c;边缘计算技术应运而生&#xff0c;而边缘计算网关作为其核心组件&#xff0c;正发挥着越来越重要的作用。边缘计算网关位于数据源和云数据中心之间。它具备数据采集、协议转换、数据处…

NLP_语言模型的雏形N-Gram

文章目录 N-Gram 模型1.将给定的文本分割成连续的N个词的组合(N-Gram)2.统计每个N-Gram在文本中出现的次数&#xff0c;也就是词频3.为了得到一个词在给定上下文中出现的概率&#xff0c;我们可以利用条件概率公式计算。具体来讲&#xff0c;就是计算给定前N-1个词时&#xff0…

打开双重el-dialog后出现遮罩后如何解决?

背景&#xff1a; 打开el-dialog后&#xff0c;再次打开另外一个el-dialog&#xff0c;出现以下画面。 解决方式&#xff1a;在第二个el-dialog增加append-to-body <el-dialog :close-on-click-modal“true” :visible.sync“createVisible” v-if“createVisible” :width…

ReactNative实现弧形拖动条

我们直接看效果 先看下面的使用代码 <CircularSlider5step{2}min{0}max{100}radius{100}value{30}onComplete{(changeValue: number) > this.handleEmailSbp(changeValue)}onChange{(changeValue: number) > this.handleEmailDpd(changeValue)}contentContainerStyle{…

Vue中nextTick方法的作用与原理

在Vue的开发中&#xff0c;你可能会遇到一些异步更新的问题&#xff0c;如在改变数据后需要等待DOM更新完毕后再进行下一步操作。这时就可以使用Vue提供的nextTick方法来解决这个问题。 nextTick方法的作用是在DOM更新之后执行回调函数&#xff0c;确保在下次DOM更新循环结束之…

nginx slice模块的使用和源码分析

文章目录 1. 为什么需要ngx_http_slice_module2. 配置指令3. 加载模块4. 源码分析4.1 指令分析4.2 模块初始化4.3 slice模块的上下文4.2 $slice_range字段值获取4.3 http header过滤处理4.4 http body过滤处理5 测试和验证 1. 为什么需要ngx_http_slice_module 顾名思义&#…

【开源】基于JAVA+Vue+SpringBoot的数据可视化的智慧河南大屏

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示四、核心代码4.1 数据模块 A4.2 数据模块 B4.3 数据模块 C4.4 数据模块 D4.5 数据模块 E 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的数据可视化的智慧河南大屏&#xff0c;包含了GDP、…

Cadence——输出文件部分

本文章基于凡亿教育基础入门66讲 &#xff08;一&#xff09;MARK点&#xff0c;工艺边&#xff0c;阻抗说明相关准备文件 &#xff08;1&#xff09;MARK点 a,点击设置&#xff0c;用户偏好设置 b,指定MARK焊盘路径和封装路径 c,点击放置&#xff0c;手动 d,点击高级设置,将…

【快速上手QT】01-QWidgetQMainWindow QT中的窗口

总所周知&#xff0c;QT是一个跨平台的C图形用户界面应用程序开发框架。它既可以开发GUI程序&#xff0c;也可用于开发非GUI程序&#xff0c;当然我们用到QT就是要做GUI的&#xff0c;所以我们快速上手QT的第一篇博文就讲QT的界面窗口。 我用的IDE是VS2019&#xff0c;使用QTc…