使用 GLib 列表¶
GLib 提供了几种用于存储数据集合的容器类型:GList、GSList、GPtrArray 和 GArray。
过去,在所有需要存储数据序列或集合的情况下,使用 GList 是一种常见的做法。 这样做是不明智的:在大多数情况下,您应该始终优先使用 GPtrArray。 GPtrArray 具有更低的内存开销(相当于列表的三分之一到一半),更好的缓存局部性,以及所有常见操作的相同或更低的算法复杂度。 只有在处理有序数据时,GList 才可能更合适,而这些数据需要在数组的任意索引处进行代价高昂的插入。 但是,如果您的有序数据集很大,您可能需要使用 GSequence,它是一种更高效的数据存储。
如果使用链表,请务必保持其操作的复杂度较低,使用标准的计算机科学复杂度分析。 任何使用 g_list_nth() 或 g_list_nth_data() 的操作几乎肯定是不正确的。
例如,对 GList 进行迭代应该使用链接指针来实现,而不是递增索引
// 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
}
// some_list is a GList defined elsewhere
for (List l = some_list; l != null; l = l.next) {
YourDataType element_data = l.data;
// Do something with @element_data
}
// or
foreach (YourDataType element_data in some_list) {
// Do something with @element_data
}
使用递增索引会导致性能二次方下降(O(N²) 而不是 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
}
// some_list is a GList defined elsewhere
// List.length () will iterate the list to determine its length
for (int i = 0; i < some_list.length (); i++) {
// List.nth_data () will iterate the list to retrieve the data
YourDataType element_data = some_list.nth_data (i);
// Do something with @element_data
}
上述代码中的性能损失来自 g_list_length() 和 g_list_nth_data(),它们都遍历列表以执行其操作。
使用 GPtrArray 实现上述内容与第一个(正确)GList 示例具有相同的复杂度,但具有更好的缓存局部性和更低的内存消耗,因此对于大量元素,性能会更好
// 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
}
// some_array is a GPtrArray defined elsewhere
for (int i = 0; i < some_array.length; i++) {
YourDataType element_data = some_array.get (i);
// Do something with @element_data
}