Using GLib Lists

GLib provides several container types for sets of data: GList, GSList, GPtrArray and GArray.

It has been common practice in the past to use GList in all situations where a sequence or set of data needs to be stored. This is inadvisable: you should always prefer a GPtrArray in most situations. GPtrArray has lower memory overhead (a third to a half of an equivalent list), better cache locality, and the same or lower algorithmic complexity for all common operations. The only typical situation where a GList may be more appropriate is when dealing with ordered data, which requires expensive insertions at arbitrary indexes in the array. If your ordered data set is large, though, you may want to use GSequence, which is a much more efficient data store.

If linked lists are used, be careful to keep the complexity of operations on them low, using standard CS complexity analysis. Any operation which uses g_list_nth() or g_list_nth_data() is almost certainly wrong.

For example, iteration over a GList should be implemented using the linking pointers, rather than a incrementing index:

// some_list is a GList defined elsewhere
for (GList *l = some_list; l != NULL; l = l->next)
  {
    YourDataType *element_data = l->data;

    // Do something with @element_data
  }

Using an incrementing index instead results in a quadratic decrease in performance (O(N²) rather than O(N)):

// some_list is a GList defined elsewhere

// g_list_length() will iterate the list to determine its length
for (guint i = 0; i < g_list_length (some_list); i++)
  {
    // g_list_nth_data() will iterate the list to retrieve the data
    YourDataType *element_data = g_list_nth_data (some_list, i);

    // Do something with @element_data
  }

The performance penalty in the code above comes from g_list_length() and g_list_nth_data(), which both traverse the list to perform their operations.

Implementing the above with a GPtrArray has the same complexity as the first (correct) GList example, but better cache locality and lower memory consumption, so will perform better for large numbers of elements:

// some_array is a GPtrArray defined elsewhere
for (guint i = 0; i < some_array->len; i++)
  {
    YourDataType *element_data = g_ptr_array_index (some_array, i);

    // Do something with @element_data
  }