【tip】UThash使用技巧总结

2024-01-18  本文已影响0人  papi_k的小茅屋

首先看一个用整型值做key的哈希结构体:

//hh是内部使用的hash处理句柄,在使用过程中,只需要在结构体中定义一个UT_hash_handle类型的变量即可,不需要为该句柄变量赋值,但必须在该结构体中定义该变量。
struct hash_entry {
    int id;            /* we'll use this field as the key */
    char name[10];
    UT_hash_handle hh; /* makes this structure hashable */
};
 //用于指向保存数据的hash表,必须初始化为空,在后面的查、插等操作中,uthash内部会根据其是否为空而进行不同的操作。
struct hash_entry *head = NULL;

// 查找

struct hash_entry *tmp;
HASH_FIND_INT(head, &key, tmp) //在hash中KEY值唯一,在添加时 需要先查找,没找到就构建一个新的,如果存在 就需要更新新的值

// 添加

// 第二入参的id是上边结构体中的成员变量id,结构体里面成员定义的名字是啥,这里就原样填啥。
HASH_ADD_INT(head, id, tmp);

// 删除

// 删除宏, 从head hash表中删除user结点
HASH_DEL(head, user);

// 清空hash表,利用hash中的迭代器进行删除和打印

void DeleteAll(struct hash_entry *head)
{
    struct hash_entry *currentUser, *tmp;

    HASH_ITER(hh, head, currentUser, tmp)
    {
        printf("id:%d,name:%s\n", tmp->id, tmp->name);
        HASH_DEL(head, currentUser);  /* delete; head advances to next */
        free(currentUser);            /* optional- if you want to free */
    }
}

//如果只想删除所有项目,但不释放它们或进行每个元素的清理,则可以通过一次操作更有效地做到这一点,之后,列表头head将设置为NULL。
HASH_CLEAR(hh, head);

// 或者通过hh.next指针遍历hash表中的所有items。

// 由于hh.prev和hh.next字段的缘故,可以在哈希中向前和向后迭代。可以通过重复跟随这些指针来访问哈希中的所有项目,因此哈希也是双链表。
void print_all(struct hash_entry *head)
{
    struct hash_entry  *tmp;
    for(tmp = head; tmp != NULL; tmp = (struct hash_entry *)tmp->hh.next)
    {
        printf("id:%d,name:%s\n", tmp->id, tmp->name);
    }
}

//统计hash表中的已经存在的元素数
unsigned int numUsers = HASH_COUNT(head);

//排序,与之前总结的qsort、Cmp差不多

// 第二个参数sortFunction是指向比较函数的指针。它必须接受两个指针参数(要比较的项目),并且如果第一个项目分别在第二个项目之前,等于或之后排序,则必须返回小于零,零或大于零的int
//(这与标准C库中的strcmp或qsort使用的约定相同)。
HASH_SORT(head, sortFunction);
int sortFunction(void *a, void *b)
{
    /* compare a to b (cast a and b appropriately)
     * return (int) -1 if (a < b)  或者 return (int) a-b 升序
     * return (int)  0 if (a == b)
     * return (int)  1 if (a > b)  或者 return(int)b-a 降序
     */
}

例如:
// 字符串比较

HASH_SORT(head, nameSort);
int nameSort(struct hash_entry *a, struct hash_entry *b)
{
    return strcmp(a->name, b->name);
}

// 整数比较

HASH_SORT(head, idSort);
int idSort(struct my_struct *a, struct my_struct *b)
{
    return (a->id - b->id);
}

UThash组key方式

// 1.字符数组做key

struct hash_entry {
    char name[10]; // 字符数组做key
    int id;
    UT_hash_handle hh;
};

int test() {
    const char *names[] = {"joe", "bob", "betty", NULL};
    struct hash_entry *s;
    struct hash_entry *next;
    struct hash_entry *head = NULL;

    for (int i = 0; names[i]; ++i) {
        s = (struct hash_entry *)malloc(sizeof(struct hash_entry));
        strcpy(s->name, names[i]);
        s->id = i;
        HASH_ADD_STR(head, name, s); // 当使用字符数组做key,要使用 HASH_ADD_STR 接口添加节点
    }

    HASH_FIND_STR(head, "betty", s);
    if (s) {
        printf("betty's id is %d\n", s->id);
    }

    HASH_ITER(hh, head, s, next) {
        HASH_DEL(head, s);
        free(s);
    }
    return 0;
}

// 2.字符指针做key

struct hash_entry {
    char *name; // 字符指针做key
    int id;
    UT_hash_handle hh;
};

int test() {
    const char *names[] = {"joe", "bob", "betty", NULL};
    struct hash_entry *s;
    struct hash_entry *next;
    struct hash_entry *head = NULL;

    for (int i = 0; names[i]; ++i) {
        s = (struct hash_entry *)malloc(sizeof(struct hash_entry));
        s->name = names[i];
        s->id = i;
        HASH_ADD_KEYPTR(hh, head, s->name, strlen(s->name), s); // 当使用字符指针做key,要使用 HASH_ADD_KEYPTR 接口添加节点
    }

    HASH_FIND_STR(head, "betty", s);
    if (s) {
        printf("betty's id is %d\n", s->id);
    }

    HASH_ITER(hh, head, s, next) {
        HASH_DEL(head, s);
        free(s);
    }
    return 0;
}

// 3.void *方式组key

// 在uthash中也可使用地址做key进行hash操作,使用地址作为key值时,其类型为void *,这样它就可以支持任意类型的地址了。
struct hash_entry {
    void *key; // void *方式组key
    int i;
    UT_hash_handle hh;
};

struct hash_entry *head = NULL;
char *someaddr = NULL;

int test() {
    struct hash_entry *s;
    struct hash_entry *tmp = (struct hash_entry*)malloc(sizeof(struct hash_entry);
    tmp->key = (void*)someaddr;
    tmp->i = 1;

    HASH_ADD_PTR(hash, key, tmp); // 当使用void *做key,要使用 HASH_ADD_PTR 接口添加节点
    HASH_FIND_PTR(hash, &someaddr, s);
    if (s) {
        printf("found\n");
    }

    HASH_DEL(head, tmp);
    free(tmp);

    return 0;
}

// 4.结构体方式组key

// HASH_ADD 表示添加的键值可以是任意类型,比较通用,比如结构体
typedef struct {
    char a;
    int b;
} RecordKey;

typedef struct {
    RecordKey key; // 结构体方式组key
    UT_hash_handle hh;
} Record;

int main(int argc, char *argv[]) {
    Record l, *p, *r, *next, *head = NULL;
    r = (Record *)malloc(sizeof *r);
    /* 结构体键值清零 */
    memset(r, 0, sizeof *r);
    r->key.a = 'a';
    r->key.b = 1;
    HASH_ADD(hh, head, key, sizeof(RecordKey), r);

    memset(&l, 0, sizeof(Record));
    l.key.a = 'a';
    l.key.b = 1;
    HASH_FIND(hh, head, &l.key, sizeof(RecordKey), p);

    if (p) {
        printf("found %c %d\n", p->key.a, p->key.b);
    }

    HASH_ITER(hh, head, p, next) {
        HASH_DEL(head, p);
        free(p);
    }
    return 0;
}

yo peace!

上一篇 下一篇

猜你喜欢

热点阅读