哈希桶封装unordered_map、unordered_set

news2024/12/28 8:44:01

哈希桶源代码

我们将由下列的哈希桶来模拟封装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;
    };
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【YOLOv7改进系列】简化YOLOv7-tiny池化层,便于引入改进的池化层

〇、前言 相比YOLOv5/v7&#xff0c;除了YOLOv5n外&#xff0c;YOLOv7tiny的参数量较小&#xff0c;效果往往也相较YOLOv5n高上不少&#xff0c;又近来博主打算改进yolov7-tiny文件&#xff0c;但苦于其池化层部位是直接写在yaml中的&#xff0c;修改极为不便&#xff0c;因此…

使用安装包安装tomcat

Apache Tomcat 是一个开源的 Java 服务器&#xff0c;用于运行 Java Servlet、JavaServer Pages (JSP) 和相关的 Java 平台技术。它是一个轻量级的、灵活的容器&#xff0c;用于在 Java 环境中部署和管理 Web 应用程序。 以下是 Tomcat 的一些主要特点和功能&#xff1a; 1.Ser…

QNX 7.0.0开发总结

1 QNX编译 1.1 基本概念 QNX可以直接使用Linux Makefile编译库和二进制&#xff0c;在Makefile文件中指定CCaarch64-unknown-nto-qnx7.0.0-g&#xff0c;或者CCx86_64-pc-nto-qnx7.0.0-g&#xff0c;保存退出后&#xff0c;运行source /qnx_sdk_path/qnxsdp-env.sh&#xff0c;…

Day50 动态规划part09

LC198打家劫舍 偷前一家或者偷前两家和这家&#xff1a;dp[i] Math.max(dp[i-2]nums[i],dp[i-1]);代码 LC213打家劫舍II&#xff08; 未掌握&#xff09; 解题思路&#xff1a;因为成环了&#xff0c;所以首位元素一定是两者只能选择一个或者两者都不选三种情况&#xff1…

Android 11 低电量自动关机失效

Android 11 低电量自动关机 概述 安卓系统设计了低电关机功能&#xff0c;旨在当手机电池电量过低时自动关机&#xff0c;以保护手机硬件和数据安全。该功能由以下几个部分组成&#xff1a; 电池电量监测: 安卓系统通过 BatteryService 组件持续监测电池电量。BatteryService…

展厅设计中的不同区域划分

1、公共区域 公共区域一般来说是不受限制的区域&#xff0c;这种情况下&#xff0c;会使我们想到的区域是大厅、售卖区、视频播放等&#xff0c;这些公共区域的相关设施比较完善&#xff0c;只是需要普通的安全保护设施及警报设备即可。 2、展览区域 展览区域是参观者能够触及到…

创新指南|2024企业如何开启生成式AI创新?从5大应用场景和6步抓手

想要了解如何采用生成式AI来提高企业效率和竞争力&#xff1f;本指南将介绍如何采用生成式AI来实现数字化转型&#xff0c;并打造智能化商业模式。从5大应用场景和6大步骤切入&#xff0c;让您了解如何开启生成式AI创新。立即连线创新专家咨询或观看创新战略方案视频进一步了解…

业务扩张阶段

和之前相比就是服务器的数量增多了 业务系统增多了 每个业务的用户也在增多 采购费用和电费挺多 选课系统一年只用几次&#xff0c;平时不用太浪费服务器的资源&#xff0c;那么怎么才能提高服务器资源的利用率呢 在一个服务器上部署多个不同的业务系统能行吗 不太行&…

基于大模型 Gemma-7B 和 llama_index,轻松实现 NL2SQL

节前&#xff0c;我们星球组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、参加社招和校招面试的同学. 针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 汇总合集&…

生产问题排查:springboot项目启动时注册nacos失败或运行时从nacos闪退

文章目录 一、引出问题二、解决方案1、使用actuator健康检查2、项目启动时判断nacos是否正常连接3、k8s设置探针 一、引出问题 生产项目是用k8s部署的&#xff0c;最近经常遇到启动时注册不到nacos&#xff08;查找nacos的host地址找不到&#xff09;&#xff0c;或者运行的好…

Unity 之 代码修改材质球贴图

Unity 之 代码修改材质球贴图 代码修改Shader&#xff1a;ShaderGraph&#xff1a;材质球包含属性 代码修改 meshRenderer.material.SetTexture("_Emission", texture);Shader&#xff1a; ShaderGraph&#xff1a; 材质球包含属性 materials[k].HasProperty("…

NXP i.MX8系列平台开发讲解 - 3.14 Linux 之Power Supply子系统(一)

专栏文章目录传送门&#xff1a;返回专栏目录 Hi, 我是你们的老朋友&#xff0c;主要专注于嵌入式软件开发&#xff0c;有兴趣不要忘记点击关注【码思途远】 目录 1. Power Supply子系统介绍 2. Power Supply子系统框架 3. Power Supply代码分析 本章节主要介绍Linux 下的P…

快速入门Linux及使用VSCode远程连接Linux服务器

在当前的技术环境中&#xff0c;Linux操作系统因其强大的功能和灵活性而广受欢迎。无论你是开发人员、系统管理员还是技术爱好者&#xff0c;学习Linux都是提升技术技能的重要一步。本文将介绍如何快速入门Linux&#xff0c;并使用Visual Studio Code&#xff08;VSCode&#x…

【MySQL数据库】:MySQL索引特性

目录 索引的概念 磁盘 磁盘的基本特征 MySQL与磁盘交互的基本单位 索引的理解 建立测试表 理解单个Page 理解多个Page 页目录 单页情况 多页情况 索引的数据结构 聚簇索引 VS 非聚簇索引 索引操作 创建主键索引 创建唯一索引 创建普通索引 创建全文索引 查询…

跨区域文件管控过程中 如何保障安全和效率?

跨区域文件管控是指在跨越不同地域或区域的情况下对文件进行管理和控制的过程。这种控制可能涉及多个方面&#xff0c;包括安全性、合规性和管理效率等。 为了有效进行跨区域文件管控&#xff0c;组织通常需要采取一系列策略和措施&#xff0c;例如&#xff1a; 1、加密和安全…

LeakSearch:针对网络公开凭证的安全扫描与检测工具

关于LeakSearch 在红队演戏过程中&#xff0c;往往需要获取到针对目标域的访问权限。在这个过程中&#xff0c;很多红队人员会选择使用暴露在互联网上的代理服务器来实现目标域的访问&#xff0c;那么此时就需要在互联网上收集公开暴露的凭证信息。 对于蓝队来说&#xff0c;…

项目bug1

大项目测bug的时候让输入数字&#xff0c;如果不是则捕获异常&#xff0c;提示错误&#xff0c;几段很简单的代码&#xff1a; System.out.println("请输入要存入的金额"); Scanner sc new Scanner(System.in); while(true) {try {money sc.nextInt();break;} cat…

Git从入门到放弃

由于我的Git学的不太好&#xff0c;所以为了能够将以后我的学习笔记能够整理的更好&#xff0c;我先要系统的学习一下git&#xff0c;文章由此产生。 文章笔记源自尚硅谷Git入门到精通全套教程视频内容 1 进入官网 学习新技术的第一步需要熟悉官网&#xff0c;Git也不例外。ht…

Spring 使用SSE(Server-Sent Events)学习

什么是SSE SSE 即服务器发送事件&#xff08;Server-Sent Events&#xff09;&#xff0c;是一种服务器推送技术&#xff0c;允许服务器在客户端建立连接后&#xff0c;主动向客户端推送数据。 SSE 基于 HTTP 协议&#xff0c;使用简单&#xff0c;具有轻量级、实时性和断线重…

linux中dd命令以及如何测试读写速度

dd命令详解 dd命令是一个在Unix和类Unix系统中非常常用的命令行工具&#xff0c;它主要用于复制文件和转换文件数据。下面我会详细介绍一些dd命令的常见用法和功能&#xff1a; 基本语法 dd命令的基本语法如下&#xff1a; bash Copy Code dd [option]...主要选项和参数 if…