使用 GSList 作为 GHashTable 值的不同方法

Different methods to use GSList as value of a GHashTable

给定代码片段:

#include <glib.h>
#include <stdio.h>

void print_city(gpointer value, gpointer data)
{
    printf("%s, ", value);
}
void print(gpointer key, gpointer value, gpointer data)
{
    printf("Here are some cities in %s: ", key);
    g_slist_foreach((GSList *)value, (GFunc)print_city, NULL);
    printf("\n");
}

void destroy(gpointer key, gpointer value, gpointer data)
{
    printf("Freeing a GSList, first item is %s\n", ((GSList*)value)->data);
    g_slist_free(value);
}

int main(int argc, char *argv[])
{
    GHashTable *hash = g_hash_table_new(g_str_hash, g_str_equal);

    g_hash_table_insert(hash,
            "Virginia",
            g_slist_append(g_hash_table_lookup(hash, "Virginia"), "Richmond")
    );

    g_hash_table_insert(hash,
            "Virginia",
            g_slist_append(g_hash_table_lookup(hash, "Virginia"), "Keysville")
    );

    g_hash_table_insert(hash,
            "Texas",
            g_slist_append(g_hash_table_lookup(hash, "Texas"), "Houston")
    );

    g_hash_table_insert(hash,
            "Texas",
            g_slist_append(g_hash_table_lookup(hash, "Texas"), "Austin")
    );

    g_hash_table_foreach(hash, print, NULL);
    g_hash_table_foreach(hash, destroy, NULL);
    g_hash_table_destroy(hash);
    return 0;
}

我们可以注意到 main 函数中调用 g_hash_table_insert 的行看起来有点复杂,重复自身,因此我正在寻找代码替代方案来简化 value 参数。考虑到这一点,我第一次尝试更改代码如下(已添加评论)

GSList *list = NULL;

// Adding the first element to `list`
// list contains the pointer to the GSList head
list = g_slist_append(list, "Austin");

// Inserting the list (an address  to a GSLIST) into
// the value of "Texas"  key.
g_hash_table_insert(hash, "Texas", list);

// Retrieves the content of "Texas"  key.
// (That is right now the pointer to the  GSList head)
// and stores into value pointer
list = g_hash_table_lookup(hash, "Texas");

// Add the string "Houston" to the GSList and stores 
// the new head at `value` to keep track of head.
list = g_slist_append(list, "Houston");

// Updates the 'key' Texas with list.
g_hash_table_insert(hash, "Texas", list);

看起来并没有好多少。我还在代码中添加了一些注释来描述我的理解。

问题一:我的理解(在评论中表达)是否正确?

问题2:保持代码的一致性,请问上面的代码有什么改进的方法吗?

我注意到我可以在第一个之后删除对 g_hash_table_insert 的所有调用,这可能会牺牲一些一致性。本例中的代码如下所示

GSList *list = NULL;

list = g_slist_append(list, "Austin");
g_hash_table_insert(hash, "Texas", list);
list = g_hash_table_lookup(hash, "Texas");
list = g_slist_append(list, "Houston");

问题 3:此更改如何影响一致性?

问题 4:这种变化会产生哪些行为?

无论如何,考虑到之前的代码片段,我试图让事情变得更多 "organized" 因此我到达了以下代码片段以更新 GHashTable 中的列表:

GSList *list = NULL;
g_hash_table_insert(hash, "Texas", list);
list = g_hash_table_lookup(hash, "Texas");

list = g_slist_append(list, "Austin");
list = g_slist_append(list, "Houston");

上面的代码看起来更有条理,但在工作中完全失败(甚至在 free 调用时触发段错误)。我不明白为什么最后一个片段失败了。

问题 5:为什么会出现段错误?

问题 6: 有没有不使用初始构造的更简单的方法来处理 GHashTables 中的 GSLists

GSList *list = NULL;
g_hash_table_insert(hash, "Texas", list);
list = g_hash_table_lookup(hash, "Texas");
list = g_slist_append(list, "Austin");
list = g_slist_append(list, "Houston");

您的哈希表只在此处包含 NULL。甚至前面的示例看起来也很不稳定,实际上不能保证有效:任何时候修改列表,头指针都可能改变。

完成您似乎想要的事情的最简单方法是先构建列表,然后将其添加到哈希表中:

GSList *list = g_slist_append(NULL, "Austin");
list = g_slist_append(list, "Houston");
g_hash_table_insert(hash, "Texas", list);

但是任何时候你想修改列表你都必须将它重新插入到哈希表中...我建议重新考虑你的数据结构选择:链接列表并不是真正适合任何东西(以及 GLib 实现正如您发现的那样特别烦人)。 GSequence、GArray(甚至 GHashTable with g_hash_table_add())可能是 GLib 中合适的替代品。