Live555源码阅读笔记:哈希表的实现(C++)
😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍哈希表的实现 🍭
😎金句分享😎:🍭你不能选择最好的,但最好的会来选择你——泰戈尔🍭
⏰发布时间⏰: 2024-07-24 23:44:05
本文未经允许,不得转发!!!
目录
- 🎄一、概述
- 🎄二、HashTable 类
- 🎄三、BasicHashTable 类
- ✨3.1 插入操作
- ✨3.2 删除操作
- ✨3.3 查询操作
- ✨3.4 查询哈希表元素个数
- 🎄四、哈希表的使用
- 🎄五、总结
🎄一、概述
本文介绍Live555源码的哈希表实现,如果对哈希表不了解的,可以看这篇文章:https://blog.csdn.net/wkd_007/article/details/140655297
哈希表是存储 键值对 的一个数据结构。一个哈希表应该要提供插入元素、删除元素、查询元素的操作,而具体的哈希表实现还会出现哈希函数、处理哈希冲突、键值对相关结构体、比对键值等,我们看哈希表代码时需要格外注意这些实现。
下图是Live555哈希表相关类的关系图:
1、HashTable 是个基类,规范哈希表的接口;
2、BasicHashTable 是哈希表的具体实现;
3、TableEntry 是存储键值对的,同时它还有个指针指向自己,说明这里应该是使用 链地址法 来处理哈希冲突的。
Live555哈希表相关代码主要在这4个文件:HashTable.hh、HashTable.cpp、BasicHashTable.hh、BasicHashTable.cpp。主要涉及两个类,下面介绍具体实现。
🎄二、HashTable 类
HashTable 类声明:
class HashTable {
public:virtual ~HashTable();// The following must be implemented by a particular// implementation (subclass):static HashTable* create(int keyType);virtual void* Add(char const* key, void* value) = 0; // Returns the old value if different, otherwise 0virtual Boolean Remove(char const* key) = 0;virtual void* Lookup(char const* key) const = 0;virtual unsigned numEntries() const = 0; // Returns 0 if not foundBoolean IsEmpty() const { return numEntries() == 0; }// Used to iterate through the members of the table:class Iterator {public:// The following must be implemented by a particular// implementation (subclass):static Iterator* create(HashTable const& hashTable);virtual ~Iterator();virtual void* next(char const*& key) = 0; // returns 0 if noneprotected:Iterator(); // abstract base class};// A shortcut that can be used to successively remove each of// the entries in the table (e.g., so that their values can be// deleted, if they happen to be pointers to allocated memory).void* RemoveNext();// Returns the first entry in the table.// (This is useful for deleting each entry in the table, if the entry's destructor also removes itself from the table.)void* getFirst(); protected:HashTable(); // abstract base class
};
首先,我们看到 HashTable 类声明了4个纯虚函数:
virtual void* Add(char const* key, void* value) = 0;// Returns the old value if different, otherwise 0
virtual Boolean Remove(char const* key) = 0;
virtual void* Lookup(char const* key) const = 0;
virtual unsigned numEntries() const = 0;
这说明 HashTable 类是个抽象基类,所以它不会实例化对象,同时很可能会使用 HashTable 类的指针或引用去管理其派生类对象。这四个函数对应了哈希表的插入、删除、查询、哈希值总个数 4个操作,这里也规定了子类必须按照这样的声明去实现这几个操作。
其次,注意到static HashTable* create(int keyType);
,说明很大可能使用这个函数来创建哈希表,并且这里的返回值是 HashTable*
,但HashTable
是抽象类,不会有对象,这里的指针应该指向派生类 BasicHashTable 的对象。函数参数 keyType 说明创建哈希表需要指定键值类型,HashTable.hh给出了两个键值类型:
int const STRING_HASH_KEYS = 0;
int const ONE_WORD_HASH_KEYS = 1;
再者,HashTable 类声明了一个迭代器类 Iterator ,并且要求传入哈希表引用,应该是用来遍历哈希表的。
最后就是 RemoveNext 操作删除一个哈希表元素,getFirst 操作获取第一个哈希表元素。
🎄三、BasicHashTable 类
BasicHashTable 类是哈希表的具体实现,它实现了哈希表的基本操作:插入、删除、查询。也实现了哈希函数、键值比对、哈希表扩容等函数,不过这些都是隐藏(private)的。
BasicHashTable 类声明如下:
class BasicHashTable: public HashTable {
private:class TableEntry; // forwardpublic:BasicHashTable(int keyType);virtual ~BasicHashTable();// Used to iterate through the members of the table:class Iterator; friend class Iterator; // to make Sun's C++ compiler happyclass Iterator: public HashTable::Iterator {// 迭代器,用于迭代传入的哈希表 2024-07-22 23:43:05public:Iterator(BasicHashTable const& table);private: // implementation of inherited pure virtual functionsvoid* next(char const*& key); // returns 0 if noneprivate:BasicHashTable const& fTable;unsigned fNextIndex; // index of next bucket to be enumerated after thisTableEntry* fNextEntry; // next entry in the current bucket};private: // implementation of inherited pure virtual functions// 实现继承的纯虚函数,这里是 private 的,也就是说不能通过 BasicHashTable 对象直接调用,但可以通过父类指针或引用来多态调用 2024-07-24virtual void* Add(char const* key, void* value);// Returns the old value if different, otherwise 0virtual Boolean Remove(char const* key);virtual void* Lookup(char const* key) const;// Returns 0 if not foundvirtual unsigned numEntries() const;private:class TableEntry {public:TableEntry* fNext;char const* key;void* value;};TableEntry* lookupKey(char const* key, unsigned& index) const;// returns entry matching "key", or NULL if noneBoolean keyMatches(char const* key1, char const* key2) const;// used to implement "lookupKey()"TableEntry* insertNewEntry(unsigned index, char const* key);// creates a new entry, and inserts it in the tablevoid assignKey(TableEntry* entry, char const* key);// used to implement "insertNewEntry()"void deleteEntry(unsigned index, TableEntry* entry);void deleteKey(TableEntry* entry);// used to implement "deleteEntry()"void rebuild(); // rebuilds the table as its size increasesunsigned hashIndexFromKey(char const* key) const;// used to implement many of the routines aboveunsigned randomIndex(uintptr_t i) const {return (unsigned)(((i*1103515245) >> fDownShift) & fMask);}private:TableEntry** fBuckets; // pointer to bucket arrayTableEntry* fStaticBuckets[SMALL_HASH_TABLE_SIZE];// used for small tablesunsigned fNumBuckets, fNumEntries, fRebuildSize, fDownShift, fMask;int fKeyType;
};
首先,BasicHashTable类继承自HashTable类,并且实现了继承的全部纯虚函数,所以BasicHashTable类不是抽象类,可以实例化对象。BasicHashTable类有点特别,只提供了创建对象接口(BasicHashTable
)、销毁对象接口(~BasicHashTable
),其他函数都是私有的,即使构造了对象,也没有公有接口可以使用,让用户只能通过基类(HashTable)指针或引用去使用基类声明为公有的几个纯虚函数,然后通过多态调用到 BasicHashTable 类的相应接口。
构造函数:
#define REBUILD_MULTIPLIER 3
BasicHashTable::BasicHashTable(int keyType): fBuckets(fStaticBuckets), fNumBuckets(SMALL_HASH_TABLE_SIZE),fNumEntries(0), fRebuildSize(SMALL_HASH_TABLE_SIZE*REBUILD_MULTIPLIER),fDownShift(28), fMask(0x3), fKeyType(keyType) {for (unsigned i = 0; i < SMALL_HASH_TABLE_SIZE; ++i) {fStaticBuckets[i] = NULL;}
}
哈希表最初创建时,只是用个包含3个元素的数组。
在阅读下面小节之前,先了解 BasicHashTable 存储键值对的结构体,除了key、value,还包含了一个 fNext 指针:
class TableEntry {
public:TableEntry* fNext;char const* key;void* value;
};
✨3.1 插入操作
BasicHashTable类插入操作通过 Add 函数实现,代码如下:
// 添加一个键值对到哈希表,2024-07-22 23:49:05
void* BasicHashTable::Add(char const* key, void* value) {void* oldValue;unsigned index;TableEntry* entry = lookupKey(key, index);if (entry != NULL) {// There's already an item with this keyoldValue = entry->value;} else {// There's no existing entry; create a new one:entry = insertNewEntry(index, key);oldValue = NULL;}entry->value = value;// If the table has become too large, rebuild it with more buckets:if (fNumEntries >= fRebuildSize) rebuild();return oldValue;
}
先查找 key 值是否已存在了,是的话就替换并返回旧的value值,不存在就插入新的键值对(insertNewEntry)。下面看看Add依赖的几个函数:lookupKey、hashIndexFromKey、keyMatches、insertNewEntry、assignKey、rebuild。
lookupKey 函数:根据key找到一个键值对(entry),下标index
// 根据key找到一个键值对(entry),下标index,2024-07-22 23:36:55 BasicHashTable::TableEntry* BasicHashTable ::lookupKey(char const* key, unsigned& index) const {TableEntry* entry;index = hashIndexFromKey(key);for (entry = fBuckets[index]; entry != NULL; entry = entry->fNext) {if (keyMatches(key, entry->key)) break;}return entry; }
先计算出哈希地址(hashIndexFromKey),再比对键值,找到键值对 entry。
hashIndexFromKey函数:哈希函数,根据key生成哈希地址index
// 哈希函数,根据key生成一个下标 2024-07-22 23:07:25 unsigned BasicHashTable::hashIndexFromKey(char const* key) const {unsigned result = 0;if (fKeyType == STRING_HASH_KEYS) {while (1) {char c = *key++;if (c == 0) break;result += (result<<3) + (unsigned)c;}result &= fMask;printf("result = %d\n",result);} else if (fKeyType == ONE_WORD_HASH_KEYS) {result = randomIndex((uintptr_t)key); } else {unsigned* k = (unsigned*)key;uintptr_t sum = 0;for (int i = 0; i < fKeyType; ++i) {sum += k[i];}result = randomIndex(sum);}return result; }
按照不同键值类型去生成哈希地址。
keyMatches 函数:比对两个键值是否相等,相等返回true。
// 比较两个key是否一致 2024-07-22 23:35:24 Boolean BasicHashTable ::keyMatches(char const* key1, char const* key2) const {// The way we check the keys for a match depends upon their type:if (fKeyType == STRING_HASH_KEYS) {return (strcmp(key1, key2) == 0);} else if (fKeyType == ONE_WORD_HASH_KEYS) {return (key1 == key2);} else {unsigned* k1 = (unsigned*)key1;unsigned* k2 = (unsigned*)key2;for (int i = 0; i < fKeyType; ++i) {if (k1[i] != k2[i]) return False; // keys differ}return True;} }
insertNewEntry函数:生成一个entry, 插入到链表,并给entry->key赋值
// 生成一个entry, 插入到链表,并给entry->key赋值,2024-07-22 23:34:42 BasicHashTable::TableEntry* BasicHashTable ::insertNewEntry(unsigned index, char const* key) {TableEntry* entry = new TableEntry();entry->fNext = fBuckets[index];fBuckets[index] = entry;++fNumEntries;assignKey(entry, key);return entry; }
这里使用头插法,将新的entry插入到 fBuckets[index] 的位置。
assignKey函数: 根据键值类型、key值,创建一个 Key,并赋值到entry的key字段
// 根据键值类型、key值,创建一个 Key,并赋值到entry的key字段 2024-07-22 21:16:20 void BasicHashTable::assignKey(TableEntry* entry, char const* key) {// The way we assign the key depends upon its type:if (fKeyType == STRING_HASH_KEYS) {entry->key = strDup(key);} else if (fKeyType == ONE_WORD_HASH_KEYS) {entry->key = key;} else if (fKeyType > 0) {unsigned* keyFrom = (unsigned*)key;unsigned* keyTo = new unsigned[fKeyType];for (int i = 0; i < fKeyType; ++i) keyTo[i] = keyFrom[i];entry->key = (char const*)keyTo;} }
rebuild 函数:重新建立一个哈希表
// 重新建立一个哈希表,2024-07-22 21:22:08 void BasicHashTable::rebuild() {// Remember the existing table size:unsigned oldSize = fNumBuckets;TableEntry** oldBuckets = fBuckets;// Create the new sized table:fNumBuckets *= 4;fBuckets = new TableEntry*[fNumBuckets];for (unsigned i = 0; i < fNumBuckets; ++i) {fBuckets[i] = NULL;}fRebuildSize *= 4;fDownShift -= 2;fMask = (fMask<<2)|0x3;// Rehash the existing entries into the new table:for (TableEntry** oldChainPtr = oldBuckets; oldSize > 0;--oldSize, ++oldChainPtr) {for (TableEntry* hPtr = *oldChainPtr; hPtr != NULL;hPtr = *oldChainPtr) {*oldChainPtr = hPtr->fNext;unsigned index = hashIndexFromKey(hPtr->key);hPtr->fNext = fBuckets[index];fBuckets[index] = hPtr;}}// Free the old bucket array, if it was dynamically allocated:if (oldBuckets != fStaticBuckets) delete[] oldBuckets; }
当哈希表键值对个数太多时,就会重建哈希表,避免因个数太多降低查询效率。
✨3.2 删除操作
BasicHashTable 的删除操作通过 Remove 函数实现,代码如下:
// 删除一个键值对(entry),2024-07-22 23:49:08
Boolean BasicHashTable::Remove(char const* key) {unsigned index;TableEntry* entry = lookupKey(key, index);if (entry == NULL) return False; // no such entrydeleteEntry(index, entry);return True;
}
1、先根据key找到键值对entry、index;
2、再删除键值对 (deleteEntry)。
deleteEntry 函数:删除指定下标的一个键值对(Entry)
// 删除指定下标的一个键值对(Entry),2024-07-22 21:20:18 void BasicHashTable::deleteEntry(unsigned index, TableEntry* entry) {TableEntry** ep = &fBuckets[index]; // 找到存在数组的位置Boolean foundIt = False;while (*ep != NULL) { // 遍历链表if (*ep == entry) {foundIt = True;*ep = entry->fNext;break;}ep = &((*ep)->fNext);}if (!foundIt) { // shouldn't happen #ifdef DEBUGfprintf(stderr, "BasicHashTable[%p]::deleteEntry(%d,%p): internal error - not found (first entry %p", this, index, entry, fBuckets[index]);if (fBuckets[index] != NULL) fprintf(stderr, ", next entry %p", fBuckets[index]->fNext);fprintf(stderr, ")\n"); #endif}--fNumEntries;deleteKey(entry);delete entry; }
1、先找到存在数组的位置;
2、遍历链表,找到键值对entry;
3、删除键值(deleteKey);
deleteKey函数:删除一个键值对(Entry)的Key
// 删除一个键值对(Entry)的Key,2024-07-22 21:21:12 void BasicHashTable::deleteKey(TableEntry* entry) {// The way we delete the key depends upon its type:if (fKeyType == ONE_WORD_HASH_KEYS) {entry->key = NULL;} else {delete[] (char*)entry->key;entry->key = NULL;} }
✨3.3 查询操作
BasicHashTable 的查询操作通过 Lookup 函数实现,代码如下:
// 查找,根据key找到value,2024-07-22 23:48:20
void* BasicHashTable::Lookup(char const* key) const {unsigned index;TableEntry* entry = lookupKey(key, index);if (entry == NULL) return NULL; // no such entryreturn entry->value;
}
通过key找到键值对entry,然后将value返回。
✨3.4 查询哈希表元素个数
BasicHashTable 的查询元素个数通过 numEntries 函数实现,代码如下:
// 哈希表总的键值对(entry)个数,2024-07-22 23:47:35
unsigned BasicHashTable::numEntries() const {return fNumEntries;
}
🎄四、哈希表的使用
这个小节,写个示例来使用上面介绍的哈希表,完整源码也上传了。下载地址:https://download.csdn.net/download/wkd_007/89576315
// g++ *.cpp -o HashTest#include <iostream>
#include "BasicHashTable.hh"
using namespace std;void printHashTable(HashTable const& hashTable)
{HashTable::Iterator *itor = HashTable::Iterator::create(hashTable);void* value = NULL;const char* pKey = NULL;while((value = itor->next(pKey)) != NULL){const char* value_str = (const char*)value;cout << "Key=" << pKey << ", Value=" << value_str << endl;}delete itor;
}int main()
{HashTable *pHashTable = new BasicHashTable(STRING_HASH_KEYS);pHashTable->Add("testA",(void*)"testA");pHashTable->Add("testB",(void*)"testB");pHashTable->Add("testC",(void*)"testC");pHashTable->Add("testD",(void*)"testD");pHashTable->Add("testE",(void*)"testE");pHashTable->Add("",(void*)"testF"); // 空字符串也可以const char *pTestA = (const char*)pHashTable->Lookup("testA");cout << "testA=" << pTestA << endl;cout << endl;printHashTable(*pHashTable);cout << endl;pHashTable->Add("",(void*)"testG"); // 替换空字符串的值printHashTable(*pHashTable);cout << endl;delete pHashTable;return 0;
}
🎄五、总结
👉本文介绍了Live555的哈希表实现,最后给出了使用例子,对于想了解哈希表实现或Live555源码的同学有一定的帮助。
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁
相关文章:

Live555源码阅读笔记:哈希表的实现(C++)
😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C、数据结构、音视频🍭 🤣本文内容🤣&a…...

警务平台app
智慧公安以大数据、云计算、人工智能、物联网和移动互联网技术为支撑,以“打、防、管、控”为目的,综合研判为核心,共享信息数据资源,融合业务功能,构建公安智慧大数据平台,实现公安信息数字化、网络化和智…...

Java代理模式详解
Java代理模式详解 概念 代理模式是一种设计模式,为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。在Java中,代理…...

docker centos镜像 npm安装包时报错“npm ERR! code ECONNRESET”
1.采用新的镜像地址 npm config set registry https://registry.npmmirror.com2.清理缓存 npm cache clean --force3.安装yarn npm install -g yarn4. 安装模块 在node_modules 同级目录执行下面命令: yarn add napi-build-utils env-paths express ejs cors …...

Angular中component和directive的区别?
在Angular中,Component和Directive都是重要的构建块,用于构建和组织应用程序的UI。然而,它们有不同的用途和特点。以下是Component和Directive的主要区别: Component(组件) 1、定义:Component…...

Unity 资源 之 Pop It 3D 解压玩具与双人AI游戏 Unity 资源包分享
精彩呈现:Pop It 3D 解压玩具与双人AI游戏 Unity 资源包分享 一、Pop It 3D 解压玩具的魅力二、双人游戏的互动乐趣三、Unity 游戏资源包的优势四、如何获取资源包 亲爱的游戏爱好者们,今天为大家带来一款令人兴奋的游戏资源——Pop It 3D 解压玩具双人带…...

linux离线安装mysql8(单机版)
文章目录 一、检查服务器是否有残留mysql资源,有的话就全删除1.1、查询mysql已安装的相关依赖:1.2、查找含有MySQL的目录 二、安装2.1、上传mysql安装包到文件夹下并解压2.2、移动及重命名2.3、mysql用户2.4、配置mysql所需的my.cnf文件2.5、给my.cnf配置…...

【Python】快速创建一个简易 HTTP 服务器(http.server)
目录 官方文档安装教程用命令行创建编写代码创建 实例 官方文档 http.server 警告: http.server 不推荐用于生产环境。它仅仅实现了 basic security checks 的要求。 安装 Python3 内置标准模块,无需安装。(在之前的 Python2 版本名称是 Si…...

随着软件开发方法的不断演进,Cobol 程序如何适应敏捷开发和持续集成/持续部署(CI/CD)的流程?
Cobol是一种古老的编程语言,最初设计用于商业数据处理。虽然它不是为敏捷开发和CI/CD流程而设计的,但仍然可以通过一些技术和方法来使其与这些现代开发流程兼容。 以下是一些方法可以帮助Cobol程序适应敏捷开发和CI/CD流程: 拆分和模块化&am…...

nodejs - MongoDB 学习笔记
一、简介 1、MongoDB 是什么 MongoDB 是一个基于分布式文件存储的数据库,官方地址 https://www.mongodb.com/ 2、数据看是什么 数据库(DataBase)是按照数据结构来组织、存储和管理数据的应用程序。 3、数据库的作用 主要作用是 管理数据…...

photoshop学习笔记——移动工具
移动工具,可以对图层进行移动,快捷键 V 使用的素材已经放上了,直接下载即可 按住ctrl 可以自动选取,鼠标点击哪个对象,自动选中哪个图层 按住 shift 校正角度(只能沿着直线移动) 按住 alt 拖…...

HarmonyOS 质量、测试、上架速浏
1.应用质量要求: 1. 应用体验质量建议: 功能数据完备 功能完备 数据完备 基础体验要求 基础约束 兼容性 稳定性 性能 功耗 安全…...

TS的访问修饰符有哪些
如果你和我一样是从强类型语言(如C、C#、Java)转过来的,相信你会一眼就知道是什么 public(默认) - 全部可访问 protected - 自己和派生类可访问 private - 只有自己可访问 废话不多说,上代码: class Person {publ…...

网络安全之扫描探测阶段攻防手段(二)
扫描探测 扫描探测阶段是攻击者对目标网络进行深入了解的关键步骤,同时也是防御者识别潜在威胁和加强安全防护的机会。 攻击端:技术原理和工具 端口扫描: 原理:攻击者使用端口扫描工具来识别目标网络中开放的端口,这…...

C++:泛型算法了解
什么是泛型算法 泛型算法是C标准模板库(STL)中的一部分,它们表示的是可以用于不同类型的元素和多种容器类型的一些经典算法的公共接口。这些算法之所以被称为“泛型”,是因为它们可以操作在多种容器类型上,包括但不限…...

基于bert的自动对对联系统
目录 概述 演示效果 核心逻辑 使用方式 1.裁剪数据集 根据自己的需要选择 2.用couplet数据集训练模型 模型存储在model文件夹中 3.将模型转换为ONNX格式 4.打开index.html就可以在前端使用此自动对对联系统了。 本文所涉及所有资源均在传知代码平台可获取。 概述 这个生成器利用…...

js-vue中多个按钮状态选中类似于复选框与单选框实现
1.vue中多个按钮状态选中类似于复选框 在Vue中处理多个按钮的选中状态切换,通常我们会利用Vue的响应式数据系统来追踪每个按钮的选中状态。 html <div id"app"> <button v-for"button in buttons" :key"button.id" :c…...

ObservableCollection新增数据前判断数据是否存在
public class MyDataModel {public int Id { get; set; }public string Name { get; set; }}public static void Main(){// 创建 ObservableCollectionObservableCollection<MyDataModel> myDataCollection new ObservableCollection<MyDataModel>{new MyDataMode…...

DBus快速入门
DBus快速入门 参考链接: 中文博客: https://www.e-learn.cn/topic/1808992 https://blog.csdn.net/u011942101/article/details/123383195 https://blog.csdn.net/weixin_44498318/article/details/115803936 https://www.e-learn.cn/topic/1808992 htt…...

SQL Server 设置端口号:详细步骤与注意事项
目录 一、了解SQL Server端口号的基础知识 1.1 默认端口号 1.2 静态端口与动态端口 二、使用SQL Server配置管理器设置端口号 2.1 打开SQL Server配置管理器 2.2 定位到SQL Server网络配置 2.3 修改TCP/IP属性 2.4 重启SQL Server服务 三、注意事项 3.1 防火墙设置 3…...

Python面试题:结合Python技术,如何使用NetworkX进行复杂网络分析
NetworkX 是一个强大的 Python 库,用于创建、操作和研究复杂网络的结构、动力学和功能。它提供了丰富的功能来处理图和网络数据,适合用于复杂网络分析。以下是使用 NetworkX 进行复杂网络分析的基本步骤: 安装 NetworkX: pip inst…...

【C#/C++】C#调C++的接口,给C++传结构体数组
C#调C的接口,给C传结构体数组 1、背景2、实现 1、背景 C#软件创建了一个结构体数组用来存储图像的区域信息,分别是矩形框的左上像素的xy坐标和矩形框右下像素的xy坐标。需要传入给调用的C函数的参数列表中,我们选择使用C#传入一个结构体数组…...

ctfshow SSTI注入 web369--web372
web369 这把request过滤了,只能自己拼字符了 ""[[__clas,s__]|join] 或者 ""[(__clas,s__)|join] 相当于 ""["__class__"]举个例子,chr(97) 返回的是字符 a,因为 97 是小写字母 a 的 Unicode 编码…...
Llama + Dify,在你的电脑搭建一套AI工作流
theme: smartblue 点赞 关注 收藏 学会了 本文简介 最近字节在推Coze,你可以在这个平台制作知识库、制作工作流,生成一个具有特定领域知识的智能体。 那么,有没有可能在本地也部署一套这个东西呢?这样敏感数据就不会泄露了&…...
洛谷 P9854 [CCC 2008 J1] Body Mass Index
这题让我们计算出 BMI 值,随后判断属于哪个等级。 BMI 值计算公式: 。 BMI 范围 对应信息 …...

Redis面试三道题目
针对Redis的面试题,我将从简单到困难给出三道题目,并附上参考答案的概要。 1. 简单题:请简述Redis是什么,以及它的主要优点。 参考答案: Redis简介:Redis是一个开源的、使用ANSI C语言编写、支持网络、可…...

redis的使用场景-分布式锁
使用redis的setnx命令放入数据并用此数据当锁完成业务(但是如果用户操作途中出现异常导致超出指定时间会出现问题) Service public class StockService {Autowiredprivate StockDao stockDao; //mapper注入Autowiredprivate StringRedisTemplate redisT…...

知识库系统全解析:2024年最佳9款
本文将分享9款优质团队知识库管理工具:PingCode、Worktile、石墨文档、语雀、Wolai 我来、有道云笔记、飞书文档、Confluence、Notion。 在追求高效团队运作的今天,掌握和整合知识成为了企业不可或缺的需求。但面对市场上琳琅满目的知识库管理工具&#…...

猫头虎分享:Numpy知识点一文带你详细学习np.random.randn()
🐯 猫头虎分享:Numpy知识点一文带你详细学习np.random.randn() 摘要 Numpy 是数据科学和机器学习领域中不可或缺的工具。在本篇文章中,我们将深入探讨 np.random.randn(),一个用于生成标准正态分布的强大函数。通过详细的代码示…...

QT 关于QTableWidget的常规使用
目录 一、初始化 二、封装功能用法 三、结语 一、初始化 1、设置表头 直接在ui设计界面修改或者使用QT封装的函数修改,代码如下: QStringList recList {"第一列", "第二列", "第三列"}; ui->tableWidget->setH…...