哈希桶封装unordered_map、unordered_set

哈希桶源代码

我们将由下列的哈希桶来模拟封装STL库中的unordered_map和unordered_set

注意:为了实现封装unordered_map和unordered_set,我们需要对下列源码进行优化。

//哈希桶
namespace hashbucket
{
    template<class K,class V>
    struct HashNode
    {
        HashNode* _next;
        pair<K, V> _kv;

        HashNode(const pair<K, V>& kv)

            :_kv(kv)
            ,_next(nullptr)
            {}
    };


    template<class K,class V>
    class HashTables
    {
        typedef HashNode<K, V> Node;
    public:

        //构造函数
        HashTables()
        {
            _tables.resize(10);
        }

        //析构函数
        ~HashTables()
        {
            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;
            }
        }

        //插入函数
        bool Insert(const pair<K, V>& kv)
        {
            if (Find(kv.first))
            {
                return false;
            }

            //负载因子
            if (_n == _tables.size())//因子到1开始扩容
            {
                //开新表
                vector<Node*> newtables;
                newtables.resize(_tables.size() * 2, nullptr);
                //遍历旧表
                for (size_t i = 0; i < _tables.size(); ++i)
                {
                    Node* cur = _tables[i];
                    while (cur)
                    {
                        Node* next = cur->_next;//记录下一个的地址
                        size_t hash = cur->_kv.first % newtables.size();//计算哈希值
                        //头插
                        cur->_next = newtables[i];
                        newtables[i] = cur;
                        //更新下一个位置
                        cur = next;
                    }
                    //将表置空
                    _tables[i] = nullptr;
                }
                //交换新旧表
                _tables.swap(newtables);
            }
            size_t hash = kv.first % _tables.size();//计算哈希值
            Node* newnode = new Node(kv);//创建结点
            //头插
            newnode->_next = _tables[hash];
            _tables[hash] = newnode;
            ++_n;
            return true;
        }

        //查找函数
        Node* Find(const K& key)
        {
            size_t hash = key % _tables.size();//计算哈希值
            Node* cur = _tables[hash];//寻找位置

            while (cur)//cur不为空则继续寻找
            {
                if (cur->_kv.first == key)//相同则找到
                {
                    return cur;//返回找到的地址
                }
                //不相同则判断下一个
                cur = cur->_next;
            }
            //出循环还没找到则返回空
            return NULL;
        }

        //删除函数
        bool Erase(const K& key)
        {
            size_t hash = key % _tables.size();//计算哈希值
            Node* prev = nullptr;//记录前地址
            Node* cur = _tables[hash];//记录当前地址
            while (cur)//不为空则继续寻找
            {
                if (cur->_kv.first == key)//相同则找到
                {
                    if (prev == nullptr)//如果为头删
                    {
                        _tables[hash] = cur->_next;//将下一个结点地址放到指针数组上
                    }
                    else
                    {
                        prev->_next = cur->_next;//将前一个结点连接后一个地址
                    }
                    delete cur;//删除找到的结点
                    return true;
                }
                prev = cur;
                cur = cur->_next;
            }
            //出循环还没找到则删除失败
            return false;
        }

    private:
        vector<Node*> _tables;
        size_t _n = 0;
    };
}

哈希桶的模板参数

这是原始模板:

    template<class K, class V>
    class HashTables

这是优化后的模板:

    template<class K, class T,class KeyofT>
    class HashTables

可以看到,这将V变为T,然后多出了KeyofT,这是什么意思呢? 

(V->T请看下面,KeyofT请看仿函数阶段)

class V    --->    class T

首先最基本的:set是K模型,map是KV模型

在set容器中,T是对应着key:

    template<class K>
    class unordered_set
    {
    public:
    //...
    private:
        hashbucket::HashTables<K, K, SetKeyofT> _ht;
    };

 在map容器中,T是对应着key和value组成的键值对:

    template<class K,class V>
    class unordered_map
    {
    public:
    //...
    private:
        hashbucket::HashTables<K, pair<const K, V>, MapKeyofT> _ht;
    };

所以模板T实际的类型是取决于上层使用的是K还是pair<K,V> 

这一切的一切都是为了让哈希桶能够适配两种不同的容器。

 

所以,哈希桶的模板参数改变后,那么结点类的模板参数也需要跟着改变了。(看下面标题) 

结点类的模板参数实现

优化前:

    template<class K,class V>
    struct HashNode
    {
        HashNode* _next;
        pair<K, V> _kv;
 
        HashNode(const pair<K, V>& kv)
 
            :_kv(kv)
            ,_next(nullptr)
            {}
    };

优化后: 

    //结点
    template<class T>
    struct HashNode
    {
        HashNode* _next;
        T _data;

        HashNode(const T& data)

            :_data(data)
            , _next(nullptr)
        {}
    };

可以看到,这里的_data就是原本的kv键值对数据,而T对应set中的key,map中的kv键值对。 

那么,class KeyofT呢?这里就要说到仿函数了

unordered_map、unordered_set中的仿函数

在unordered_map和unordered_set容器中各自的私有函数分别有着:

它们分别传入底层哈希桶时,T传入的可能是key,也可能是key和value的键值对,如果是键值对,那么就需要将键值对的key提取出来再进行比较,那么此时就需要用到仿函数来提取key。

        //map容器
        struct MapKeyofT
        {
            const K& operator()(const pair<K,V>& kv)
            {
                return kv.first;
            }
        };

        //set容器
        struct SetKeyofT
        {
            const K& operator()(const K& key)
            {
                return key;
            }
        };

可以看到,我们在这个仿函数中重载了operator(), 这个operator()在map中用来提取kv.first,也就是key值,为了能统一map和set,我们在set也重载了operator()。

所以set传入底层哈希桶就是set的仿函数,map传入底层哈希桶就是map的仿函数。

迭代器类的实现

先查看下列代码:

    //解决冲突的前置声明
    template<class K, class T, class KeyofT>
    class HashTables;

    //迭代器
    template<class K,class T,class Ref, class Ptr, class KeyofT>
    struct HTiterator
    {
        typedef HashNode<T> Node;//哈希结点的类型
        typedef HTiterator<K, T, Ref, Ptr, KeyofT> Self;//迭代器类型
        Node* _node;//结点指针

        const HashTables<K, T, KeyofT>* _pht;//迭代器要哈希表,哈希表要迭代器,冲突
        //vector<Node*>* _ptb;//直接使用私有类,就不会冲突了

        size_t _hash;//用来计算哈希值

    };

可以看到这里有一个用来解决冲突的前置声明,因为在后续使用迭代器时,我们需要用到哈希表类型,但是这个迭代器类是放在哈希表上面,编译器会往上寻找,找不到,那么就会报错,此时这种情况就是,哈希表需要用到迭代器,迭代器需要用到哈希表,两者冲突了,为了解决这种情况,我们加了个前置声明哈希表,告诉编译器是存在的,往下找就好了。 

构造函数

        //构造函数
        HTiterator(Node* node, HashTables<K, T, KeyofT>* pht, size_t hash)
            :_node(node)
            , _pht(pht)
            , _hash(hash)
        {}
        //const构造函数
        HTiterator(Node* node, const HashTables<K, T, KeyofT>* pht, size_t hash)
            :_node(node)
            , _pht(pht)
            , _hash(hash)
        {}

 *函数重载

        Ref operator*()
        {
            return _node->_data;//对地址的解引用,返回对应数据即可
        }

->函数重载 

        Ptr operator->()
        {
            return &_node->_data;//返回数据地址的引用
        }

!=函数重载 

        bool operator!=(const Self& s)
        {
            return _node != s._node;//判断两个结点的地址是否不同
        }

==函数重载 

bool operator==(const Self& s) const
{
	return _node == s._node; //判断两个结点的地址是否相同
}

 ++函数重载

        Self& operator++()
        {
            if (_node->_next)//如果结点的下一个位置不为空
            {
                _node = _node->_next;//继续往下走
            }
            else//如果结点的下一个位置为空
            {
                //开始重新寻找下一个桶
                ++_hash;//哈希值++往后寻找
                
                while (_hash < _pht->_tables.size())//当哈希值不超过表的大小的话循环
                {
                    //如果哈希值对应的位置不为空,那么就找到了
                    if (_pht->_tables[_hash])
                    {
                        _node = _pht->_tables[_hash];//更新结点位置
                        break;//停止循环
                    }
                    //如果为空,出了判定条件,那么哈希值继续自增
                    ++_hash;
                }
                //如果哈希值超过了表的大小,那么说明没有了,让结点置空
                if (_hash == _pht->_tables.size())
                {
                    _node = nullptr;
                }
            }
            return *this;
        }

迭代器函数的实现

        typedef HTiterator<K, T, T&, T*, KeyofT> iterator;
        typedef HTiterator<K, T, const T&, const T*, KeyofT> const_iterator;

        iterator begin()
        {
            从表头开始寻找,直到找到第一个不为空的位置,返回该迭代器
            for (size_t i = 0; i < _tables.size(); ++i)
            {
                if (_tables[i])
                {
                    return iterator(_tables[i], this, i);
                }
            }
            //如果没找到那么就直接返回空,调用end()即可
            return end();
        }

        iterator end()
        {
            //返回nullptr
            return iterator(nullptr, this, -1);
        }

        const_iterator begin() const
        {
            for (size_t i = 0; i < _tables.size(); ++i)
            {
                if (_tables[i])
                {
                    return const_iterator(_tables[i], this, i);
                }
            }
            return end();
        }

        const_iterator end() const
        {
            return const_iterator(nullptr, this, -1);
        }

优化之后的哈希桶代码

//哈希桶
namespace hashbucket
{
    //结点
    template<class T>
    struct HashNode
    {
        HashNode* _next;
        T _data;

        HashNode(const T& data)

            :_data(data)
            , _next(nullptr)
        {}
    };

    //解决冲突的前置声明
    template<class K, class T, class KeyofT>
    class HashTables;

    //迭代器
    template<class K,class T,class Ref, class Ptr, class KeyofT>
    struct HTiterator
    {
        typedef HashNode<T> Node;
        typedef HTiterator<K, T, Ref, Ptr, KeyofT> Self;
        Node* _node;

        const HashTables<K, T, KeyofT>* _pht;//迭代器要哈希表,哈希表要迭代器,冲突
        //vector<Node*>* _ptb;//直接使用私有类,就不会冲突了

        size_t _hash;

        HTiterator(Node* node,HashTables<K,T,KeyofT>* pht,size_t hash)
            :_node(node)
            ,_pht(pht)
            ,_hash(hash)
        {}

        HTiterator(Node* node, const HashTables<K, T, KeyofT>* pht, size_t hash)
            :_node(node)
            , _pht(pht)
            , _hash(hash)
        {}

        Self& operator++()
        {
            if (_node->_next)
            {
                _node = _node->_next;
            }
            else
            {
                ++_hash;
                while (_hash < _pht->_tables.size())
                {
                    if (_pht->_tables[_hash])
                    {
                        _node = _pht->_tables[_hash];
                        break;
                    }
                    ++_hash;
                }
                if (_hash == _pht->_tables.size())
                {
                    _node = nullptr;
                }
            }
            return *this;
        }

        Ref operator*()
        {
            return _node->_data;
        }

        Ptr operator->()
        {
            return &_node->_data;
        }

        bool operator!=(const Self& s)
        {
            return _node != s._node;
        }
    };


    //哈希表
    template<class K, class T,class KeyofT>
    class HashTables
    {
        typedef HashNode<T> Node;

        //友元函数,让外部类能访问私有成员
        template<class K, class T, class Ref, class Ptr, class KeyofT>
        friend struct HTiterator;

    public:
        
        typedef HTiterator<K, T, T&, T*, KeyofT> iterator;
        typedef HTiterator<K, T, const T&, const T*, KeyofT> const_iterator;

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

        iterator end()
        {
            return iterator(nullptr, this, -1);
        }

        const_iterator begin() const
        {
            for (size_t i = 0; i < _tables.size(); ++i)
            {
                if (_tables[i])
                {
                    return const_iterator(_tables[i], this, i);
                }
            }
            return end();
        }

        const_iterator end() const
        {
            return const_iterator(nullptr, this, -1);
        }

        //构造函数
        HashTables()
        {
            _tables.resize(10);
        }

        //析构函数
        ~HashTables()
        {
            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;
            }
        }

        //插入函数
        pair<iterator,bool> Insert(const T& data)
        {
            KeyofT kot;
            iterator it = Find(kot(data));

            if (it != end())
            {
                return make_pair(it,false);
            }

            //负载因子
            if (_n == _tables.size())//因子到1开始扩容
            {
                //开新表
                vector<Node*> newtables;
                newtables.resize(_tables.size() * 2, nullptr);
                //遍历旧表
                for (size_t i = 0; i < _tables.size(); ++i)
                {
                    Node* cur = _tables[i];
                    while (cur)
                    {
                        Node* next = cur->_next;//记录下一个的地址
                        size_t hash = kot(cur->_data) % newtables.size();//计算哈希值
                        //头插
                        cur->_next = newtables[i];
                        newtables[i] = cur;
                        //更新下一个位置
                        cur = next;
                    }
                    //将表置空
                    _tables[i] = nullptr;
                }
                //交换新旧表
                _tables.swap(newtables);
            }
            size_t hash = kot(data) % _tables.size();//计算哈希值
            Node* newnode = new Node(data);//创建结点
            //头插
            newnode->_next = _tables[hash];
            _tables[hash] = newnode;
            ++_n;
            return make_pair(iterator(newnode,this,hash), true);
        }

        //查找函数
        iterator Find(const K& key)
        {
            KeyofT kot;
            size_t hash = key % _tables.size();//计算哈希值
            Node* cur = _tables[hash];//寻找位置

            while (cur)//cur不为空则继续寻找
            {
                if (kot(cur->_data) == key)//相同则找到
                {
                    return iterator(cur,this,hash);//返回找到的地址
                }
                //不相同则判断下一个
                cur = cur->_next;
            }
            //出循环还没找到则返回空
            return end();
        }

        //删除函数
        bool Erase(const K& key)
        {
            KeyofT kot;
            size_t hash = key % _tables.size();//计算哈希值
            Node* prev = nullptr;//记录前地址
            Node* cur = _tables[hash];//记录当前地址
            while (cur)//不为空则继续寻找
            {
                if (kot(cur->_data) == key)//相同则找到
                {
                    if (prev == nullptr)//如果为头删
                    {
                        _tables[hash] = cur->_next;//将下一个结点地址放到指针数组上
                    }
                    else
                    {
                        prev->_next = cur->_next;//将前一个结点连接后一个地址
                    }
                    delete cur;//删除找到的结点
                    return true;
                }
                prev = cur;
                cur = cur->_next;
            }
            //出循环还没找到则删除失败
            return false;
        }

    private:
        vector<Node*> _tables;
        size_t _n = 0;
    };

}



用哈希桶封装unordered_map的代码

#pragma once
#include"hashtable.h"

namespace bear
{
    template<class K,class V>
    class unordered_map
    {
        struct MapKeyofT
        {
            const K& operator()(const pair<K,V>& kv)
            {
                return kv.first;
            }
        };
    public:
        typedef typename hashbucket::HashTables<K, pair<const K, V>, MapKeyofT>::iterator iterator;
        typedef typename hashbucket::HashTables<K, pair<const K, V>, MapKeyofT>::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 = _ht.Insert(make_pair(key, V()));
            return ret.first->second;
        }

        V& operator[](const K& key) const
        {
            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:
        hashbucket::HashTables<K, pair<const K, V>, MapKeyofT> _ht;
    };
}

用哈希桶封装unordered_set的代码

#pragma once
#include"hashtable.h"

namespace bear
{
    template<class K>
    class unordered_set
    {
        struct SetKeyofT
        {
            const K& operator()(const K& key)
            {
                return key;
            }
        };
    public:
        typedef typename hashbucket::HashTables<K, K, SetKeyofT>::const_iterator iterator;
        typedef typename hashbucket::HashTables<K, K, SetKeyofT>::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<const_iterator,bool> Insert(const K& key)
        {
            auto ret = _ht.Insert(key);
            return pair<const_iterator, bool>(const_iterator(ret.first._node,ret.first._pht,ret.first._hash),ret.second);
        }

        iterator Find(const K& key)
        {
            return _ht.Find(key);
        }
        bool Erase(const K& key)
        {
            return _ht.Erase(key);
        }

    private:
        hashbucket::HashTables<K, K, SetKeyofT> _ht;
    };
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

双列集合基础知识

package exercise;import java.util.HashMap; import java.util.Map;public class Demo1 {public static void main(String[] args) {Map<String, String> map new HashMap<>();//在添加数据的时候&#xff0c;如果键不存在&#xff0c;那么直接把键值对对象添加到…

Linux下线程的互斥与同步详解

&#x1f916;个人主页&#xff1a;晚风相伴-CSDN博客 &#x1f496;如果觉得内容对你有帮助的话&#xff0c;还请给博主一键三连&#xff08;点赞&#x1f49c;、收藏&#x1f9e1;、关注&#x1f49a;&#xff09;吧 &#x1f64f;如果内容有误或者有写的不好的地方的话&…

go语言实战--基于Vue3+gin框架的实战Cetide网项目(讲解开发过程中的各种踩坑)

最近被要求学习go语言开发&#xff0c;也就做一个项目实战巩固一下&#xff0c;也分享一下关于gin框架的实战项目 &#xff08;后续应该还是会继续学习Java&#xff0c;这一期还是做一个govue的&#xff09; 经过一段时间的开发过后&#xff0c;感觉现在的开发效率要快不少了&…

你可以直接和数据库对话了!DB-GPT 用LLM定义数据库下一代交互方式,数据库领域的GPT、开启数据3.0 时代

✨点击这里✨&#xff1a;&#x1f680;原文链接&#xff1a;&#xff08;更好排版、视频播放、社群交流、最新AI开源项目、AI工具分享都在这个公众号&#xff01;&#xff09; 你可以直接和数据库对话了&#xff01;DB-GPT 用LLM定义数据库下一代交互方式&#xff0c;数据库领…

如何理解与学习数学分析——第二部分——数学分析中的基本概念——第6章——级数

第2 部分&#xff1a;数学分析中的基本概念 (Concepts in Analysis) 6. 级数(Series) 本章从等比级数(geometric series)开始&#xff0c;研究可以使用公式计算无限和的条件。它讨论了部分和与级数收敛的符号、图形表示和定义&#xff0c;并将它们应用于调和级数。它介绍了级…

EKF在LiFePO4电池SOC估算中不好用?一问带你破解EKF应用难题

磷酸铁锂电池因为平台区的存在&#xff0c;导致使用戴维南模型EKF的方法时&#xff0c;无法准确进行SOC准确预估。所以最近搜索了大量关于磷酸铁锂电池SOC预估的论文、期刊&#xff0c;但我被海量忽略客观事实、仅为了毕业的硕士论文给震惊到了。很多论文为了掩饰平台区的存在&…

Live800:深度解析,客户服务如何塑造品牌形象

在当今竞争激烈的市场环境中&#xff0c;品牌形象对于企业的成功至关重要。而客户服务作为品牌与消费者之间最直接的互动方式&#xff0c;不仅影响着消费者的购买决策&#xff0c;更在塑造品牌形象方面发挥着不可替代的作用。本文将深度解析客户服务如何塑造品牌形象&#xff0…

python文件:py,ipynb, pyi, pyc, pyd, pyo都是什么文件?

1、Python文件类型介绍 &#x1f4c1; 1.1 .py 文件&#xff1a;源代码基础 .py 文件是 Python 最基本的源代码文件格式&#xff0c;用于存储纯文本形式的 Python 代码。它是开发者编写程序的主要场所&#xff0c;包含函数、类、变量定义以及执行逻辑。Python 解释器直接读取…

C++ OpenCV 图像分类魔法:探索神奇的模型与代码

⭐️我叫忆_恒心&#xff0c;一名喜欢书写博客的研究生&#x1f468;‍&#x1f393;。 如果觉得本文能帮到您&#xff0c;麻烦点个赞&#x1f44d;呗&#xff01; 近期会不断在专栏里进行更新讲解博客~~~ 有什么问题的小伙伴 欢迎留言提问欧&#xff0c;喜欢的小伙伴给个三连支…

React hooks动态配置侧边栏

React hooks根据不同需求 还有不同的角色 动态的去配置侧边栏 需求&#xff1a; 点击某个按钮是一套侧边栏 &#xff0c;不同角色&#xff08;比如管理员之类的权限高一点&#xff09;比普通用户多个侧边栏 然后点击另一个按钮是另一套侧边栏 此时&#xff0c;就需要动态的去…

解决微信小程序分享按钮不可用

问题描述 在微信小程序中点击胶囊按钮上的三个点&#xff0c;在弹出的对话框中的【分享给好友】【分享到朋友圈】按钮都属于不可用的状态&#xff0c;显示未设置。 问题截图 解决方案 在每个需要此功能的页面都需要添加此代码&#xff0c;否则就不能进行使用。 // vue3时&l…

基础乐理入门

基础概念 乐音&#xff1a;音高&#xff08;频率&#xff09;固定&#xff0c;振动规则的音。钢琴等乐器发出的是乐音&#xff0c;听起来悦耳、柔和。噪音&#xff1a;振动不规则&#xff0c;音高也不明显的音。风声、雨声、机器轰鸣声是噪音&#xff0c;大多数打击乐器&#…

在UI界面中实现3d人物展示

简要原理(设置双摄像机): 为需要展示的3D人物单独设置一个摄像机(只设置为渲染人物层级),主要摄像机的方向与人物方向一致,但摄像机需要需要旋转180,设置的角度自行进行微调创建一个Render Texture类型的组件用于存储摄像机渲染的内容UI上设置需要展示的图片区域,图片…

台湾合泰原装BS66F360 封装LQFP-44 电容触摸按键 AD+LED增强型触控

BS66F360是一款由Holtek Semiconductor Inc.生产的微控制器&#xff08;microcontroller&#xff09;&#xff0c;具有触摸检测和LED驱动功能。其应用领域广泛&#xff0c;包括但不限于以下几个方面&#xff1a; 1. 触摸按键应用&#xff1a;BS66F360内置了触摸按键检测功能&am…

【MySQL】聊聊MySQL常见的SQL语句阻塞场景

在平时的业务中&#xff0c;可能一个简单的SQL语句也执行很慢&#xff0c;这种情况其实大多数都是要么没有使用索引&#xff0c;要么出现锁竞争造成执行阻塞。本篇主要来介绍具体的场景 CREATE TABLE t ( id int(11) NOT NULL, c int(11) DEFAULT NULL, PRIMARY KEY (id) ) ENG…

17.调用游戏本身的hp减伤害函数实现秒杀游戏角色

上一个内容&#xff1a;16.在目标进程构建CALL执行代码 16.在目标进程构建CALL执行代码在它的代码上进行的更改&#xff0c;它的callData变量中的代码不完善一个完整的函数是有return的&#xff0c;处理器执行到return会返回如果执行不到会继续往下走&#xff0c;直到执行不下…

像素着色技术在AI绘画中的革新作用

摘要&#xff1a;随着人工智能技术的不断进步&#xff0c;AI绘画已成为艺术和技术领域中的一个热门话题。本文将探讨像素着色技术在AI绘画中的应用及其对创作过程的影响&#xff0c;揭示这一技术如何推动艺术创作的革新。 引言&#xff1a; 传统的绘画方法要求艺术家具备高超的…

Nextjs学习教程

一.手动创建项目 建议看这个中文网站文档,这个里面的案例配置都是手动的,也可以往下看我这个博客一步步操作 1.在目录下执行下面命令,初始化package.json文件 npm init -y2.安装react相关包以及next包 yarn add next react react-dom // 或者 npm install --save next react…

kafka的leader和follower

leader和follower kafka的leader和follower是相对于分区有意义的&#xff0c;不是相对于broker。 因为每个分区都有leader和follower, leader负责读写数据。 follower负责复制leader的数据保存到自己的日志数据中&#xff0c;并在leader挂掉后重新选举出leader。 kafka会再…

【Unity】 HTFramework框架(五十一)代码片段执行器

更新日期&#xff1a;2024年6月8日。 Github源码&#xff1a;[点我获取源码] Gitee源码&#xff1a;[点我获取源码] 索引 Code Snippet Executer 代码片段执行器使用 Code Snippet Executer打开 Code Snippet Executer动态执行&#xff08;代码片段&#xff09;静态执行&#x…