GLib のハッシュテーブル(再挑戦)!

前回 の続きです。今回は需要の大きい C 言語用のハッシュテーブルです。

GLib すごいけど、よく考えるとライセンスが LGPL なのでちょっと気をつけないといけません。

下のサンプルコードで色んな API を使ってみましたが、これで全部ではありません。

#include <glib.h>
#include <glib/gprintf.h>

/*
 * コンパイルの仕方:
 *   gcc -Wall -pedantic `pkg-config --cflags --libs glib-2.0` hoge.c
 *
 * API リファレンス:
 *   https://developer.gnome.org/glib/2.40/glib-Hash-Tables.html
 */

void destroy_key(gpointer key);
void destroy_val(gpointer val);
void print_key_val(gpointer key, gpointer val, gpointer user_data);

int main(int argc, char *argv[])
{
  GHashTable *population;
  gpointer key;  /* gpointer は総称ポインタ */
  gpointer val;

  GList *keys;
  GList *vals;
  GList *item;

  guint size;
  gchar **keysp;

  gint i;

  GHashTableIter iter;
  
  /* ハッシュテーブルの作成
   * 第一引数はキーをハッシュする関数、第二引数はキーを比較する関数
   * ハッシュ関数には g_direct_hash, g_int_hash, g_int64_hash,
   * g_double_hash, g_str_hash がある
   * 比較関数には g_direct_equal, g_int_equal, g_int64_equal,
   * g_double_equal, g_str_equal がある
   * キーやバリューを自動的に破壊する (free する) 必要がある場合は
   * g_hash_table_new_full を使い、デストラクタ (free 等) を指定する
   */
  population = g_hash_table_new(g_str_hash, g_str_equal);

  /* 要素の追加
   * ここでは gint 値を直接ポインタ型にキャストしている
   *
   * 追加したいキーが既にハッシュテーブルに存在する場合の挙動:
   *   g_hash_table_insert は古いバリューを破壊する
   *   g_hash_table_replace は古いバリューとキーを破壊する
   */
  g_hash_table_insert(population, "横浜市",
      GINT_TO_POINTER(3702225));
  g_hash_table_insert(population, "大阪市",
      GINT_TO_POINTER(2682140));
  g_hash_table_insert(population, "名古屋市",
      GINT_TO_POINTER(2272075));
  g_hash_table_insert(population, "札幌市",
      GINT_TO_POINTER(1921237));
  g_hash_table_insert(population, "神戸市",
      GINT_TO_POINTER(1539546));

  /* 要素の取得 */
  key = (gpointer)"横浜市";
  val = g_hash_table_lookup(population, key);
  g_printf("%s: %d\n", (gchar*)key, GPOINTER_TO_INT(val));

  /* 要素の削除
   * デストラクタを呼ばずに要素を削除したい場合は g_hash_table_steal を使う
   */
  g_hash_table_remove(population, key);
  val = g_hash_table_lookup(population, key);
  if (val == NULL) {/* g_hash_table_contains でもキーの存否が確認できる */
    g_printf("そんなキーはありません……: %s\n", (gchar*)key);
  }

  /* 全てのキーを取得する */
  keys = g_hash_table_get_keys(population);
  g_printf("\n----- 全キー一覧 -----\n");
  for (item = keys; item != NULL; item = g_list_next(item)) {
    g_printf("%s\n", (gchar*)item->data);
  }

  /* 全てのバリューを取得する */
  vals = g_hash_table_get_values(population);
  g_printf("\n----- 全バリュー一覧 -----\n");
  for (item = vals; item != NULL; item = g_list_next(item)) {
    g_printf("%d\n", GPOINTER_TO_INT(item->data));
  }

  /* 全てのキーを取得する
   * GList ではなく、配列として取得する方法
   */
  size = g_hash_table_size(population);
  /* g_new は calloc のようなもの */
  keysp = (gchar**)g_new(gpointer, size);
  /* g_hash_table_get_keys_as_array は NULL 終端の配列を返す */
  keysp = (gchar**)g_hash_table_get_keys_as_array(population,
      &size);
  g_printf("\n----- 全キー一覧 -----\n");
  for (i = 0; i < size; i++) {
    g_printf("%s\n", keysp[i]);
  }
  /* 用済みのキーの配列を free し忘れないように */
  g_free(keysp);

  /* for each */
  g_printf("\n----- 全キー・バリュー一覧 -----\n");
  i = 0;
  g_hash_table_foreach(population, print_key_val, &i);

  /* 全てのキー・バリューの組を取得する */
  g_printf("\n----- 全キー・バリュー一覧 -----\n");
  g_hash_table_iter_init(&iter, population);
  while (g_hash_table_iter_next(&iter, &key, &val)) {
    g_printf("%s => %d\n", (gchar*)key, GPOINTER_TO_INT(val));
  }

  /* イテレーションの最中にバリューを変更したり、組を削除することもできる */
  g_hash_table_iter_init(&iter, population);
  while (g_hash_table_iter_next(&iter, &key, &val)) {
    g_hash_table_iter_replace(&iter, GINT_TO_POINTER(4649));

    if (! g_strcmp0((gchar*)key, "神戸市")) {
      g_hash_table_iter_remove(&iter);
    }
  }
  g_printf("\n----- 全キー・バリュー一覧 -----\n");
  g_hash_table_iter_init(&iter, population);
  while (g_hash_table_iter_next(&iter, &key, &val)) {
    g_printf("%s => %d\n", (gchar*)key, GPOINTER_TO_INT(val));
  }

  /* ハッシュテーブルの破壊 */
  g_hash_table_destroy(population);


  /* ハッシュテーブルの作成 (デストラクタ付き) */
  population = g_hash_table_new_full(g_str_hash, g_str_equal,
      destroy_key, destroy_val);

  val = g_new(gint, 1);
  *(gint*)val = 10925;
  g_hash_table_insert(population, g_strdup("夕張市"), val);
  val = g_new(gint, 1);
  *(gint*)val = 10225;
  g_hash_table_insert(population, g_strdup("三笠市"), val);
  val = g_new(gint, 1);
  *(gint*)val = 4020;
  g_hash_table_insert(population, g_strdup("歌志内市"), val);

  g_printf("\nグッバイ、GHashTable!\n\n");
  g_hash_table_destroy(population);

  return 0;
}

void destroy_key(gpointer key)
{
  g_printf("%s を破壊します\n", (gchar*)key);
  g_free(key);
}

void destroy_val(gpointer val)
{
  g_printf("%d を破壊します\n", *(gint*)val);
  g_free(val);
}

void print_key_val(gpointer key, gpointer val, gpointer user_data)
{
  g_printf("%d: %s => %d\n", (*(gint*)user_data)++, (gchar*)key,
      GPOINTER_TO_INT(val));
}

実行結果:

横浜市: 3702225
そんなキーはありません……: 横浜市

----- 全キー一覧 -----
名古屋市
神戸市
大阪市
札幌市

----- 全バリュー一覧 -----
2272075
1539546
2682140
1921237

----- 全キー一覧 -----
札幌市
大阪市
神戸市
名古屋市

----- 全キー・バリュー一覧 -----
0: 札幌市 => 1921237
1: 大阪市 => 2682140
2: 神戸市 => 1539546
3: 名古屋市 => 2272075

----- 全キー・バリュー一覧 -----
札幌市 => 1921237
大阪市 => 2682140
神戸市 => 1539546
名古屋市 => 2272075

----- 全キー・バリュー一覧 -----
札幌市 => 4649
大阪市 => 4649
名古屋市 => 4649

グッバイ、GHashTable!

歌志内市 を破壊します
4020 を破壊します
夕張市 を破壊します
10925 を破壊します
三笠市 を破壊します
10225 を破壊します

(コウヅ)

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中