TBOX中提供了各种列表操作:
-
list: 元素在内部维护的双向链表
-
list_entry: 元素在外部维护的双向链表
-
single_list: 元素在内部维护的单向链表
-
single_list_entry: 元素在外部维护的单向链表
由于双链和单链的接口使用类似,这里主要就讲解双链的具体使用。
那什么是内部维护和外部维护呢? 简单地说:
-
外部维护:就是链表容器本身不存储元素,不开辟内存空间,仅仅是一个节点头,这样比较节省内存,更加灵活。(尤其是在多个链表间元素迁移的时候,或者多个链表需要统一内存池维护的时候)。
-
内部维护:就是链表容器本身回去开辟一块空间,去单独存储元素内容,这种方式对接口的操作比较简单,但是灵活性和性能不如前一种,如果不需要多个链表维护同一种元素,那么使用这种模式简单操作下,更为妥当。(而且内部元素的存储也是用内存池优化过的)。
list的使用
list的使用很简单,接口用起来也很方便,这里给个简单的例子:
1// 创建一个long类型的双链,参数0表示采用默认的自动元素增长大小,也可以手动设置更适合的大小 2tb_list_ref_t list = tb_list_init(0, tb_element_long()); 3if (list) 4{ 5 // 在链表头部插入元素:1,并返回这个新元素的迭代器索引 6 tb_size_t itor = tb_list_insert_head(list, (tb_pointer_t)1); 7 8 // 在之前新的元素后面插入一个新元素:2 9 tb_list_insert_next(list, itor, (tb_pointer_t)2); 10 11 // 在链表尾部插入元素:3 12 tb_list_insert_tail(list, (tb_pointer_t)3); 13 14 // 移除指定的元素 15 tb_list_remove(list, itor); 16 17 // 遍历所有链表元素, 18 tb_for_all(tb_long_t, item, list) 19 { 20 // 打印元素值 21 tb_trace_i("%ld", item); 22 } 23 24 // 销毁list 25 tb_list_exit(list); 26}
list_entry的使用
list_entry由于是外置式的容器,需要在外面自己定义的结构体上进行操作,例如定义:
1// 链表元素结构体 2typedef struct __tb_demo_entry_t 3{ 4 // 外置双链的节点,用于链表维护 5 tb_list_entry_t entry; 6 7 // 元素的实际数据 8 tb_size_t data; 9 10}tb_demo_entry_t;
对链表的具体操作如下:
1// 定义一些静态元素,用于插入链表(实际使用可能需要自己动态创建他们) 2tb_demo_entry_t entries[12] = 3{ 4 { {0}, 0 } 5, { {0}, 1 } 6, { {0}, 2 } 7, { {0}, 3 } 8, { {0}, 4 } 9, { {0}, 5 } 10, { {0}, 6 } 11, { {0}, 7 } 12, { {0}, 8 } 13, { {0}, 9 } 14, { {0}, 10} 15, { {0}, 11} 16}; 17 18// 初始化链表,需要指定外置元素的结构体类型,链表的节点名字 19tb_list_entry_head_t list; 20tb_list_entry_init(&list, tb_demo_entry_t, entry, tb_null); 21 22// 插入一些元素,注意:所有操作都是在外置结构体中的list_entry节点上操作 23tb_list_entry_insert_tail(&list, &entries[5].entry); 24tb_list_entry_insert_tail(&list, &entries[6].entry); 25tb_list_entry_insert_tail(&list, &entries[7].entry); 26tb_list_entry_insert_tail(&list, &entries[8].entry); 27tb_list_entry_insert_tail(&list, &entries[9].entry); 28tb_list_entry_insert_head(&list, &entries[4].entry); 29tb_list_entry_insert_head(&list, &entries[3].entry); 30tb_list_entry_insert_head(&list, &entries[2].entry); 31tb_list_entry_insert_head(&list, &entries[1].entry); 32tb_list_entry_insert_head(&list, &entries[0].entry); 33 34// 访问具体某个节点的元素数据 35tb_demo_entry_t* entry = (tb_demo_entry_t*)tb_list_entry(&list, &entries[5].entry); 36tb_trace_i("entry: %lu", entry->data); 37 38// 遍历所有元素 39tb_trace_i("insert: %lu", tb_list_entry_size(&list)); 40tb_for_all_if(tb_demo_entry_t*, item0, tb_list_entry_itor(&list), item0) 41{ 42 tb_trace_i("%lu", item0->data); 43} 44 45// 替换头尾的元素 46tb_list_entry_replace_head(&list, &entries[10].entry); 47tb_list_entry_replace_last(&list, &entries[11].entry); 48 49// 移除头尾的元素 50tb_list_entry_remove_head(&list); 51tb_list_entry_remove_last(&list); 52 53// 移动元素位置,这里吧头尾的元素对调了下 54tb_list_entry_ref_t head = tb_list_entry_head(&list); 55tb_list_entry_moveto_head(&list, tb_list_entry_last(&list)); 56tb_list_entry_moveto_tail(&list, head); 57 58// 退出列表 59tb_list_entry_exit(&list);
怎么样,也不是很复杂吧,由于元素的内存都在外面自己维护,所以灵活性提升了不少,并且可以多个链表同时维护,然后共用一个内存池进行优化,效率和内存都能得到最大的提升,这种模式在linux内核里面很常见。
如果要做比喻的话,list就是傻瓜式操作,list_entry就是定制化操作。。。
本文分享自微信公众号 - TBOOX开源工程(tboox-os)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。