多索引哈希表实现

Multi index hash table implementation

本文关键字:实现 哈希表 索引      更新时间:2023-10-16

我有一个需要快速查找的记录表,因此决定进行哈希表查找。

现在,最终出现的问题是,我必须基于多个键查找记录。

例如,下面的所有4个键都应指向同一记录。

key1 -> a,b,c,d,e
key2 -> a,b,d
key3 -> a,b,e
key4 -> c

问题#1然后,该模式显示出与数据库查找的相似性,其中指定了多个键。那么,B树数据结构比多哈希表设计更适合使用吗?

问题#2一个特殊的trie是否更适合这个问题。默认实现将要求所有键a+b+c+d+e作为查找键。如果我必须查找a+b+d,那么从这个主密钥将不得不跳过c&e抬起头来。但是,这个想法会奏效吗?还是已经存在了?

问题#3另一个想法是,我是否在表中插入内容,同时构建另一个查找表,并为每个记录添加索引。这样,我可以为每个键设置多个掩码,并扫描这个查找表以查找匹配的记录。我想类似于CAM表格。但若我必须扫描整张表,那个么性能就会下降。是否有可能将哈希表+索引逻辑混合在一起,以提供速度和最佳内存使用率?

到目前为止,已经尝试使用boost多索引、uthash、trie等来尝试并实现适合所有4个问题的设计,但到目前为止还没有成功。我喜欢boost多索引,但它本身也有一些问题,禁止我使用。

尽管我使用C语言进行编程和测试设计,但我对任何其他语言都很满意,比如java、php、python。

任何其他解决这个问题的想法都将不胜感激。

我想实现的解决方案的伪代码:

/* Keys */
struct key1_s {
int src;
int dst;
char name[10];
int t1;
int t2;
};
struct key2_s {
int src;
int dst;
char name[10];
};
struct key3_s {
int src;
int dst;
int t1;
};

struct key4_s {
int src;
int dst;
int t2;
};

/* Record */
struct record_s {
int src;
int dst;
char name[10];
int t1;
int t2;
int age;
int sex;
int mobile;
}
struct record_s record[2] = {
{1, 2, "jack", 5, 6, 50, 1, 1234567890},
{3, 4, "john", 7, 8, 60, 2, 1122334455}
};
table.insert(record[0]);
table.insert(record[1]);
/* search using key1 */
struct key1_s key1;
key1.src = 1;
key1.dst = 2;
strncpy(key1.name, "jack", 10);
key1.t1 = 5;
key1.t2 = 6;
table.find(key1); // should return pointer to record[0]
/* search using key2 */
struct key2_s key2;
key2.src = 1;
key2.dst = 2;
strncpy(key1.name, "jack", 10);
table.find(key2); // should return pointer to record[0]
/* search using key3 */
struct key3_s key3;
key3.src = 1;
key3.dst = 2;
key3.t1  = 5;
table.find(key3); // should return pointer to record[0]

如果find()返回一个成功的指针,那么我想更新记录的字段,比如年龄、性别、手机。

Boost Multi-Index可以在这里提供帮助。

composite_keys.cpp示例包含一个引人注目的示例。您只需要将ordered全局替换为hashed,就可以得到您正在处理的内容(此外,在您的情况下,关键配置中会有更多重叠)。

关于绩效问题,我认为没有明确的答案;它(总是)取决于使用模式。您需要分析和平衡在优化方面所花费的工作量。

我个人认为Boost Multi Index是关注便利性和快速结果的最佳选择。请注意,这绝不是说BMI没有优化(我相信它是高度优化的);然而,它将/总是/取决于使用模式。(考虑一个最初大容量插入大量数据,然后只读取的应用程序;这样的应用程序可以从一次显式构建索引中受益,而不是在每次插入时自动更新所有索引)。

查看Coliru直播

using namespace boost::multi_index;
/* A file record maintains some info on name and size as well
 * as a pointer to the directory it belongs (null meaning the root
 * directory.)
 */
struct file_entry
{
  file_entry(
    std::string name_,unsigned size_,bool is_dir_,const file_entry* dir_):
    name(name_),size(size_),is_dir(is_dir_),dir(dir_)
  {}
  std::string       name;
  unsigned          size;
  bool              is_dir;
  const file_entry* dir;
  friend std::ostream& operator<<(std::ostream& os,const file_entry& f)
  {
      os << f.name << "t" << f.size;
      if (f.is_dir)os << "t <dir>";
      return os;
  }
};
/* A file system is just a multi_index_container of entries with indices on
 * file and size (per directory). 
 */
struct dir_and_name_key:composite_key<
  file_entry,
  BOOST_MULTI_INDEX_MEMBER(file_entry,const file_entry*,dir),
  BOOST_MULTI_INDEX_MEMBER(file_entry,std::string,name)
>{};
struct dir_and_size_key:composite_key<
  file_entry,
  BOOST_MULTI_INDEX_MEMBER(file_entry,const file_entry* const,dir),
  BOOST_MULTI_INDEX_MEMBER(file_entry,unsigned,size)
>{};
typedef multi_index_container<
  file_entry,
  indexed_by<
    hashed_unique<dir_and_name_key>,
    hashed_non_unique<dir_and_size_key>
  >
> file_system;
/* typedef's of the two indices of file_system */
typedef nth_index<file_system,0>::type file_system_by_name;
typedef nth_index<file_system,1>::type file_system_by_size;
/* We build a rudimentary file system simulation out of some global
 * info and a map of commands provided to the user.
 */
static file_system fs;                 /* the one and only file system */
static file_system_by_name& fs_by_name=fs;         /* name index to fs */
static file_system_by_size& fs_by_size=get<1>(fs); /* size index to fs */
static const file_entry* current_dir=0;            /* root directory   */