Common GLIB data structures (2)

Source: Internet
Author: User

Hash table

Concept

So far, this tutorial only describes ordered containers, where the inserted entries remain in the specified order. A hash table is another type of container, also known as "ing", "associative array", or "Directory (dictionary )".

Just as the Chinese Dictionary uses a definition to associate a word, a hash table uses a key to uniquely identify a value ). The hash table can perform insert, search, and delete operations very quickly based on the key. In fact, if used properly, these operations can be a constant time-that is, O (1)-operation. This is much faster than searching for or deleting entries from an ordered list, which is an O (n) operation.

Hash Tables perform operations quickly because they use Hash Functions to Locate keys. The hash function obtains a key and calculates a unique value for it ). For example, a hash function can accept a word and return the number of letters in the word as a hash value. That is a bad hash function, because "fiddle" and "faddle" will hash the same value.

When the hash function returns the same hash value for different keys, it depends on the implementation of the hash table. A hash table can overwrite the first value with the second value, put the value in a list, or simply throw an error.

Note: The hash table is not necessarily faster than the list. If you have fewer entries-less than a dozen or so-you can use an ordered set to achieve better performance. That's because, although it takes constant time to store and retrieve data in a hash table, the constant time value may be large, it is a slow process to calculate the hash value of an entry than to reference one or two pointers. For smaller values, simply traversing an ordered list is faster than performing hash calculation.

At any time, it is important to consider the specific data storage needs of your application when selecting a container. If an application obviously needs a container, there is no reason not to use it.

Some simple hash table operations

Here are some examples to illustrate the above theory vividly:

// Ex-ghashtable-1.c
# Include <glib. h>
Int main (int argc, char ** argv ){
GHashTable * hash = g_hash_table_new (g_str_hash, g_str_equal );
G_hash_table_insert (hash, "Virginia", "Richmond ");
G_hash_table_insert (hash, "Texas", "Austin ");
G_hash_table_insert (hash, "Ohio", "Columbus ");
G_printf ("There are % d keys in the hash/n", g_hash_table_size (hash ));
G_printf ("The capital of Texas is % s/n", g_hash_table_lookup (hash, "Texas "));
Gboolean found = g_hash_table_remove (hash, "Virginia ");
G_printf ("The value 'virginia 'was % sfound and removed/n", found? "": "Not ");
G_hash_table_destroy (hash );
Return 0;
}

* ***** Output *****

There are 3 keys in the hash
The capital of Texas is Austin
The value 'virgina' was found and removed

There are many new things, so some annotations are given:

* If g_hash_table_new is specified, the hash table uses a string as the key. The g_str_hash and g_str_equal functions are built-in functions of GLib, which are very common. Other built-in hash/equivalence functions include g_int_hash/g_int_equal (using integers as keys) and g_direct_hash/g_direct_equal (using pointers as keys ).
* GLists and GSLists have a g _ [iner] _ free function to clear them. You can use g_hash_table_destroy to clear GHashTable.
* If you try to use g_hash_table_remove to delete a key/value pair, A gboolean return value is returned, indicating whether the key is found and deleted. Gboolean is a simple cross-platform GLib Implementation of true/false values.
* G_hash_table_size returns the number of middle keys in the hash table.

Insert and replace values

When g_hash_table_insert is used to insert a key, GHashTable first checks whether the key already exists. If it already exists, the value will be replaced, and the key will not be replaced. If you want to replace both keys and values, use g_hash_table_replace. It is slightly different, so the two are also shown below:

// Ex-ghashtable-2.c
# Include <glib. h>
Static char * texas_1, * texas_2;
Void key_destroyed (gpointer data)
{
G_printf ("Got a key destroy call for % s/n", data = texas_1? "Texas_1": "texas_2 ");
}
Int main (int argc, char ** argv)
{

GHashTable * hash = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) key_destroyed, NULL );
 
Texas_1 = g_strdup ("Texas ");
Texas_2 = g_strdup ("Texas ");
G_hash_table_insert (hash, texas_1, "Austin ");
G_printf ("Calling insert with the texas_2 key/n ");
G_hash_table_insert (hash, texas_2, "Houston ");
G_printf ("Calling replace with the texas_2 key/n ");
G_hash_table_replace (hash, texas_2, "Houston ");
G_printf ("Destroying hash, so goodbye texas_2/n ");
G_hash_table_destroy (hash );
G_free (texas_1 );
G_free (texas_2 );
Return 0;
}

* ***** Output *****

Calling insert with the texas_2 key
Got a key destroy call for texas_2
Calling replace with the texas_2 key
Got a key destroy call for texas_1
Destroying hash, so goodbye texas_2
Got a key destroy call for texas_2

As shown in the output, when g_hash_table_insert tries to insert a string (Texas) that is the same as the existing key, GHashTable Simply Releases the passed key (texas_2) and causes the current key (texas_1) remain unchanged. However, when g_hash_table_replace does the same thing, the texas_1 key is destroyed and the texas_2 key is used. More Annotations:

* When creating a new GHashTable, you can use g_hash_table_full to provide a GDestroyNotify implementation and call it when the key is destroyed. This allows you to completely clear resources for that key, or (in this example) view what actually happened when the key changes.
* G_strdup has already appeared in the previous GSList section. Here, it is used to allocate two copies of the string Texas. It can be found that the GHashTable functions g_str_hash and g_str_equal correctly detect that although the Pointer Points to different memory locations, the strings are actually the same. To avoid Memory leakage, you must release texas_1 and texas_2 at the end of the function. Of course, this is not important in this example because the program will exit, but it is best to clear it anyway.

Traverse Key/value pairs

It is sometimes necessary to traverse all key/value pairs. Here is how to use g_hash_table_foreach to complete the task:

// Ex-ghashtable-3.c
# Include <glib. h>
Void iterator (gpointer key, gpointer value, gpointer user_data)
{
G_printf (user_data, * (gint *) key, value );
}

Int main (int argc, char ** argv)
{
GHashTable * hash = g_hash_table_new (g_int_hash, g_int_equal );
 
Gint * k_one = g_new (gint, 1), * k_two = g_new (gint, 1), * k_three = g_new (gint, 1 );
* K_one = 1, * k_two = 2, * k_three = 3;

G_hash_table_insert (hash, k_one, "one ");
G_hash_table_insert (hash, k_two, "four ");
G_hash_table_insert (hash, k_three, "Nine ");
 
G_hash_table_foreach (hash, (ghfunc) iterator, "the square of % d is % s/n ");
G_hash_table_destroy (hash );
Return 0;
}

* ***** Output *****

The square of 1 is one
The square of 2 is four
The square of 3 is nine

There are some minor differences in this example:

* You can find that using the hash functions g_int_hash and g_int_equal provided by glib allows you to use pointers to integers as keys. This example uses the integer glib cross-platform Abstraction: Gint.
* G_hash_table_foreach is very similar to the g_slist_foreach and g_list_foreach functions you know. The only difference is that the ghfunc passed to g_hash_table_foreach must accept three parameters instead of two. In this example, a printed string used to format the key and string is passed as the third parameter. In addition, although the keys in this example are printed in the order they are inserted, there is no guarantee that the key insertion sequence will be retained.

Search for entries // (not fully understood ))))))))))) )))))))))))))))))))))

Use the g_hash_table_find function to find a specific value. This function allows you to view each key/value pair until the expected value is located. Here is an example:

// Ex-ghashtable-4.c
# Include <glib. h>
Void value_destroyed (gpointer data)
{
G_printf ("Got a value destroy call for % d/n", GPOINTER_TO_INT (data ));
}

Gboolean finder (gpointer key, gpointer value, gpointer user_data)
{
Return (GPOINTER_TO_INT (key) + GPOINTER_TO_INT (value) = 42;
}

Int main (int argc, char ** argv)
{
GHashTable * hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) value_destroyed );
 
G_hash_table_insert (hash, GINT_TO_POINTER (6), GINT_TO_POINTER (36 ));
G_hash_table_insert (hash, GINT_TO_POINTER (10), GINT_TO_POINTER (12 ));
G_hash_table_insert (hash, GINT_TO_POINTER (20), GINT_TO_POINTER (22 ));
 
Gpointer item_ptr = g_hash_table_find (hash, (GHRFunc) finder, NULL );
Gint item = GPOINTER_TO_INT (item_ptr );
G_printf ("% d + % d = 42/n", item, 42-item );
 
G_hash_table_destroy (hash );
Return 0;
}

* ***** Output *****

36 + 6 = 42
Got a value destroy call for 36
Got a value destroy call for 22
Got a value destroy call for 12

As shown in the following example, g_hash_table_find and other content are introduced:

* When GHRFunc returns TRUE, g_hash_table_find returns the first value. If GHRFunc does not return TRUE if it acts on any entry (this indicates no suitable entry is found), it returns NULL.
* This example introduces another set of built-in glib hash functions: g_direct_hash and g_direct_equal. This set of functions supports using pointers as keys, but they do not try to explain the data behind pointers. To put pointers into ghashtable, you need to use some convenient glib macros (gint_to_pointer and gpointer_to_int) to convert integers and pointers.
* Finally, a ghashtable is created in this example and a gdestroypolicy callback function is provided to check when the entries are destroyed. In most cases, you want to release some memory in a function similar to this type. However, for example, this implementation only prints a message.

Difficult to handle: delete from the table

Occasionally, you may need to delete an entry from a ghashtable, but do not obtain the callback of any gdestroypolicy function provided by ghashtable. To complete this task, you can use g_hash_table_steal based on the specific key, or use g_hash_table_foreach_steal based on all keys that match a condition.

// Ex-ghashtable-5.c
# Include <glib. h>
Gboolean wide_open (gpointer key, gpointer value, gpointer user_data)
{
Return true;
}

Void key_destroyed (gpointer data)
{
G_printf ("got a gdestroynotify callback/N ");
}

Int main (int argc, char ** argv)
{
GHashTable * hash = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) key_destroyed, (GDestroyNotify) key_destroyed );
 
G_hash_table_insert (hash, "Texas", "Austin ");
G_hash_table_insert (hash, "Virginia", "Richmond ");
G_hash_table_insert (hash, "Ohio", "Columbus ");
G_hash_table_insert (hash, "Oregon", "Salem ");
G_hash_table_insert (hash, "New York", "Albany ");
 
G_printf ("Removing New York, you shoshould see two callbacks/n ");
G_hash_table_remove (hash, "New York ");
If (g_hash_table_steal (hash, "Texas "))
{
G_printf ("Texas has been stolen, % d items remaining/n", g_hash_table_size (hash ));
}
G_printf ("Stealing remaining items/n ");
G_hash_table_foreach_steal (hash, (GHRFunc) wide_open, NULL );
G_printf ("Destroying the GHashTable, but it's empty, so no callbacks/n ");
G_hash_table_destroy (hash );
Return 0;
}

* ***** Output *****

Removing New York, you shoshould see two callbacks
Got a GDestroyNotify callback
Got a GDestroyNotify callback
Texas has been stolen, 3 items remaining
Stealing remaining items
Destroying the GHashTable, but it's empty, so no callbacks

Advanced Search: Find the key and Value

GHashTable provides a g_hash_table_lookup_extended function to obtain keys and values from the table at the same time. It is very similar to g_hash_table_lookup, but it must accept two more parameters. These are all "out" parameters; that is, they are double indirect pointers that point to the data when it is located. Here is how it works:

// Ex-ghashtable-6.c
# Include <glib. h>
Int main (int argc, char ** argv)
{
GHashTable * hash = g_hash_table_new (g_str_hash, g_str_equal );
 
G_hash_table_insert (hash, "Texas", "Austin ");
G_hash_table_insert (hash, "Virginia", "Richmond ");
G_hash_table_insert (hash, "Ohio", "Columbus ");
 
Char * state = NULL;
Char * capital = NULL;
Char ** key_ptr = & state;
Char ** value_ptr = & capital;
 
Gboolean result = g_hash_table_lookup_extended (hash, "Ohio", (gpointer *) key_ptr, (gpointer *) value_ptr );
If (result)
{
G_printf ("Found that the capital of % s is % s/n", capital, state );
}
If (! G_hash_table_lookup_extended (hash, "Vermont", (gpointer *) key_ptr, (gpointer *) value_ptr ))
{
G_printf ("couldn't find Vermont in the hash table/N ");
}
 
G_hash_table_destroy (hash );
Return 0;
}

* ***** Output *****

Found that the capital of Columbus is Ohio
Couldn't find Vermont in the hash table

Initialization of variables that can receive key/value data is somewhat complicated, but it is understandable that it is a way to return more than one value from a function. Note: If you pass NULL for one of the last two parameters, g_hash_table_lookup_extended will still work, but will not fill in the null parameter.

Multiple values for each key

So far, we have introduced hash columns with only one value for each key. However, sometimes you need a key to hold multiple values. When this requirement occurs, it is usually a good solution to use gslist as the value and add a new value with gslist. However, this requires a little more work, as shown in this example:

// Ex-ghashtable-7.c
# Include <glib. h>
Void print (gpointer key, gpointer value, gpointer data)
{
G_printf ("Here are some cities in % s:", key );
G_slist_foreach (gslist *) value, (gfunc) g_printf, null );
G_printf ("/N ");
}

Void destroy (gpointer key, gpointer value, gpointer data)
{
G_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, "Texas", g_slist_append (g_hash_table_lookup (hash, "Texas"), "Austin "));
 
G_hash_table_insert (hash, "Texas", g_slist_append (g_hash_table_lookup (hash, "Texas"), "Houston "));
 
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"), "keyville "));
 
G_hash_table_foreach (hash, print, null );
G_hash_table_foreach (hash, destroy, null );
G_hash_table_destroy (hash );
Return 0;
}
* ***** Output *****

Here are some cities in Texas: Austin Houston
Here are some cities in Virginia: Richmond keyville
Freeing a gslist, first item is Austin
Freeing a gslist, first item is Richmond

G_slist_append accepts null as a valid parameter of gslist. In this example, the "Insert a new city" code exploits this fact; it does not need to check whether this is the first city to be added to the Dingzhou list.

When the GHashTable is destroyed, remember to release the GSList before releasing the hash table itself. Note that if static strings are not used in those lists, this will be more complex; in that case, you need to release each entry in each GSList before releasing the List itself. One of the content shown in this example is how practical the foreach functions are-they can save a lot of input.

Practical Application

Here is an example of how to use GHashTables.

In Gaim:

* Gaim-1.2.1/src/buddyicon. c uses GHashTable to keep track of the friend icon (buddy icons. The key is the username of a friend, and the value is a pointer to the GaimBuddyIcon structure.
* Gaim-1.2.1/src/protocols/yahoo. c is the only place in the three applications that use g_hash_table_steal. It uses g_hash_table_steal as an integral part of the code snippet that constructs the ing from account name to friend list.

In Evolution:

* Evolution-2.0.2/smime/gui/certificate-manager.c uses GHashTable to track the root cause of S/MIME certificates; keys are Organization Names and values are pointers to GtkTreeIter.
* Evolution-data-server-1.0.2/calendar/libecal/e-cal.c uses GHashTable to track the time zone. The key is the time zone ID string and the value is the string description of an icaltimezone struct.

In GIMP:

* Gimp-2.2.4/libgimp/gimp. c Use ghashtable to track temporary processes. In the entire codebase, where g_hash_table_lookup_extended is used, g_hash_table_lookup_extended is used to locate a process, so that the memory of the hash key can be released before the process is deleted.
* Gimp-2.2.4/APP/CORE/gimp. c uses ghashtable to save the image. The key is the image ID (an integer) and the value is the pointer to the gimpimage struct.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.