ArrayList数组是基石,是完成更复杂功能的基础。几乎所有的语言所提供工具库中,都含有ArrayList数组结构,唯有C的标准库中没有提供。我的实现是工具箱的一部分,上层已经依赖完成诸多功能。所以,正确性和效率是经过验证的。
实现的核心是利用了标准库realloc的特性,实现之前我也参看过很多开源的代码和思路。使用realloc并非是最好的选择,但是最简单最容易想到的实现。可以了解一些以前写的这篇文章,realloc 使用和思考。依赖C语言泛型Array数组封装。ArrayList完成以下功能:
- 可以存放任意数据类型
- 在需要的时候动态扩充内存
- 可以自定义内存扩充策略
- 一套元素访问接口
- 一套快捷的宏定义访问
- 可缩放的容量控制
首先看定义
typedef struct
{
/**
* Increase memory space default 10
*/
int increment;
/**
* Hold ArrayList memory data
* the length is ArrayList memory capacity
*/
Array array[1];
/** Element sizeof */
int typeSize;
/** Element count */
int size;
}
ArrayList;
真正的数据容量被Array持有,size就是元素的个数,容量是Array的length。typeSize代表了元素的结构大小。
元素访问方法如下:
typedef struct
{
/** The typeSize is sizeof element */
ArrayList* (*Create) (int typeSize);
void (*Init) (int typeSize, ArrayList* outArrayList);
ArrayList* (*CreateWithSize) (int typeSize, int size);
void (*InitWithSize) (int typeSize, int size, ArrayList* outArrayList);
ArrayList* (*CreateWithCapacity)(int typeSize, int capacity);
void (*InitWithCapacity) (int typeSize, int capacity, ArrayList* outArrayList);
/**
* Release member memory space
*/
void (*Release) (ArrayList* arrayList);
/**
* Increase one element memory (typeSize data) appended to ArrayList
* return elementPtr point the increase element
*/
void* (*GetAdd) (ArrayList* arrayList);
/**
* Increase one element memory (typeSize data) at the index
* and move index behind elements one position
*
* index is which in [0, size - 1]
* return elementPtr point the increase element
*/
void* (*GetInsert) (ArrayList* arrayList, int index);
/**
* Copy element data into getAdd memory
* elementPtr point to element
* return elementPtr in ArrayList
*/
void* (*Add) (ArrayList* arrayList, void* elementPtr);
/**
* Copy element data into getInsert memory
* elementPtr point to element
* return elementPtr in ArrayList
*/
void* (*Insert) (ArrayList* arrayList, int index, void* elementPtr);
/**
* Get elementPtr point to element in [0, size - 1] of ArrayList
*
void* (*Get) (ArrayList* arrayList, int index);
*/
/**
* Copy element data into index in [0, size - 1] of ArrayList
* elementPtr point to element
*
void (*Set) (ArrayList* arrayList, int index, void* elementPtr);
*/
/**
* Remove index of element and move behind elements
*/
void (*Remove) (ArrayList* arrayList, int index);
/**
* Remove a range of elements
*/
void (*RemoveRange) (ArrayList* arrayList, int fromIndex, int toIndex);
/**
* Remove index of element and replace last element to the index
*/
void (*RemoveByEnd) (ArrayList* arrayList, int index);
/**
* Clear size to 0 and memory data rest to 0, keep memory space
*/
void (*Clear) (ArrayList* arrayList);
/**
* Release not used memory data which is after ArrayList size
* if ArrayList size is 0 will be free all memory data
*/
void (*Shrink) (ArrayList* arrayList);
/**
* Set ArrayList size and unused memory initialized 0
*/
void (*SetSize) (ArrayList* arrayList, int size);
/**
* Set ArrayList capacity and unused memory initialized 0
*/
void (*SetCapacity) (ArrayList* arrayList, int capacity);
}
_AArrayList_;
extern _AArrayList_ AArrayList[1];
实现如下:
#define CheckIndex(tag, arrayList, index) \
ALog_A(((index) < (arrayList)->size && (index) >= 0), "ArrayList " tag " error, index = %d, size = %d", index, arrayList->size);
#define CheckInsertIndex(tag, arrayList, index) \
ALog_A(((index) <= (arrayList)->size && (index) >= 0), "ArrayList " tag " error, index = %d, size = %d", index, arrayList->size);
static void AddCapacity(ArrayList* arrayList, int increment)
{
ALog_A(increment > 0, "ArrayList error, increment = %d can not <= 0", increment);
void* data = realloc(arrayList->array->data, (increment + arrayList->array->length) * arrayList->typeSize);
ALog_A
(
data,
"ArrayList error, unable to realloc memory, size = %d, length = %d, increment = %d",
arrayList->size, arrayList->array->length, increment
);
/** set increment data to 0, but it seems not very necessary
memset
(
(char*) data + arrayList->typeSize * arrayList->array->length,
0,
arrayList->typeSize * increment
);
*/
arrayList->array->data = data;
arrayList->array->length += increment;
}
static void* GetAdd(ArrayList* arrayList)
{
if (arrayList->size == arrayList->array->length)
{
AddCapacity(arrayList, arrayList->increment);
}
return (char*) arrayList->array->data + arrayList->typeSize * (arrayList->size++);
}
static void* GetInsert(ArrayList* arrayList, int index)
{
// insert index can equal size
CheckInsertIndex("GetInsert", arrayList, index);
if (arrayList->size == arrayList->array->length)
{
AddCapacity(arrayList, arrayList->increment);
}
void* from = (char*) arrayList->array->data + arrayList->typeSize * index;
void* to = (char*) from + arrayList->typeSize;
memmove(to, from, arrayList->typeSize * (arrayList->size - index));
arrayList->size++;
return from;
}
static void* Add(ArrayList* arrayList, void* elementPtr)
{
void* ptr = GetAdd(arrayList);
memcpy(ptr, elementPtr, arrayList->typeSize);
return ptr;
}
static void* Insert(ArrayList* arrayList, int index, void* elementPtr)
{
void* ptr = GetInsert(arrayList, index);
memcpy(ptr, elementPtr, arrayList->typeSize);
return ptr;
}
/** use macro instead
static void* Get(ArrayList* arrayList, int index)
{
CheckIndex("Get", arrayList, index);
return (char*) arrayList->array->data + arrayList->typeSize * index;
}
*/
/** use macro instead
static void Set(ArrayList* arrayList, int index, void* elementPtr)
{
CheckIndex("Set", arrayList, index);
memcpy((char*) arrayList->array->data + arrayList->typeSize * index, elementPtr, arrayList->typeSize);
}
*/
static void Remove(ArrayList* arrayList, int index)
{
CheckIndex("Remove", arrayList, index);
int endIndex = arrayList->size - 1;
arrayList->size = endIndex;
if (index == endIndex)
{
// remove the last element only set 0, but it seems not very necessary
// memset((char*) arrayList->array->data + arrayList->typeSize * index, 0, arrayList->typeSize);
}
else
{
void* to = (char*) arrayList->array->data + arrayList->typeSize * index;
void* from = (char*) to + arrayList->typeSize;
// move between endIndex and index element
memmove(to, from, arrayList->typeSize * (endIndex - index));
// set last element 0, but it seems not very necessary
// memset((char*) arrayList->array->data + arrayList->typeSize * arrayList->size, 0, arrayList->typeSize);
}
}
static void RemoveRange(ArrayList* arrayList, int fromIndex, int toIndex)
{
CheckIndex("RemoveRange", arrayList, fromIndex);
CheckIndex("RemoveRange", arrayList, toIndex);
ALog_A(toIndex > fromIndex, "ArrayList RemoveRange toIndex must more than fromIndex");
int num = toIndex - fromIndex + 1;
int endIndex = arrayList->size - 1;
arrayList->size -= num;
if (toIndex == endIndex)
{
// range reach the end, but it seems not very necessary
// memset((char*) arrayList->array->data + arrayList->typeSize * fromIndex, 0, arrayList->typeSize * num);
}
else
{
void* to = (char*) arrayList->array->data + arrayList->typeSize * fromIndex;
void* from = (char*) to + arrayList->typeSize * num;
// move between endIndex and toIndex element
memmove(to, from, arrayList->typeSize * (endIndex - toIndex));
// set last range elements 0, but it seems not very necessary
// memset((char*) arrayList->array->data + arrayList->typeSize * arrayList->size, 0, arrayList->typeSize * num);
}
}
static void RemoveByEnd(ArrayList* arrayList, int index)
{
CheckIndex("removeByLast", arrayList, index);
int endIndex = arrayList->size - 1;
arrayList->size = endIndex;
if (index == endIndex)
{
// remove the last element only set 0, but it seems not very necessary
// memset((char*) arrayList->array->data + arrayList->typeSize * index, 0, arrayList->typeSize);
}
else
{
memcpy
(
(char*) arrayList->array->data + arrayList->typeSize * index,
(char*) arrayList->array->data + arrayList->typeSize * endIndex,
arrayList->typeSize
);
}
}
static void Clear(ArrayList* arrayList)
{
arrayList->size = 0;
// set data 0, but it seems not very necessary
// memset(arrayList->array->data, 0, arrayList->typeSize * arrayList->size);
}
static void Shrink(ArrayList* arrayList)
{
if (arrayList->size == 0)
{
arrayList->array->length = 0;
free(arrayList->array->data);
arrayList->array->data = NULL;
}
else
{
void* data = realloc(arrayList->array->data, arrayList->size * arrayList->typeSize);
ALog_A(data, "ArrayList Shrink error, size = %d ", arrayList->size);
arrayList->array->data = data;
arrayList->array->length = arrayList->size;
}
}
static void Release(ArrayList* arrayList)
{
arrayList->size = 0;
arrayList->array->length = 0;
free(arrayList->array->data);
arrayList->array->data = NULL;
}
static void SetSize(ArrayList* arrayList, int size)
{
arrayList->size = size;
if (arrayList->array->length >= size)
{
return;
}
AddCapacity(arrayList, size - arrayList->array->length);
}
static void SetCapacity(ArrayList* arrayList, int capacity)
{
if (arrayList->array->length >= capacity)
{
return;
}
AddCapacity(arrayList, capacity - arrayList->array->length);
}
#define InitArrayList(typeSize, arrayList) \
arrayList->array->length = 0; \
arrayList->array->data = NULL; \
arrayList->typeSize = typeSize; \
arrayList->size = 0; \
arrayList->increment = 10
static ArrayList* Create(int typeSize)
{
ArrayList* arrayList = (ArrayList*) malloc(sizeof(ArrayList));
InitArrayList(typeSize, arrayList);
return arrayList;
}
static void init(int typeSize, ArrayList* outArrayList)
{
InitArrayList(typeSize, outArrayList);
}
static ArrayList* CreateWithSize(int typeSize, int size)
{
ArrayList* arrayList = Create(typeSize);
SetSize(arrayList, size);
return arrayList;
}
static void InitWithSize(int typeSize, int size, ArrayList* outArrayList)
{
InitArrayList(typeSize, outArrayList);
SetSize(outArrayList, size);
}
static ArrayList* CreateWithCapacity(int typeSize, int capacity)
{
ArrayList* arrayList = Create(typeSize);
SetCapacity(arrayList, capacity);
return arrayList;
}
static void InitWithCapacity(int typeSize, int capacity, ArrayList* outArrayList)
{
InitArrayList(typeSize, outArrayList);
SetCapacity(outArrayList, capacity);
}
_AArrayList_ AArrayList[1] =
{
Create,
init,
CreateWithSize,
InitWithSize,
CreateWithCapacity,
InitWithCapacity,
Release,
GetAdd,
GetInsert,
Add,
Insert,
// get,
// set,
Remove,
RemoveRange,
RemoveByEnd,
Clear,
Shrink,
SetSize,
SetCapacity,
};
#undef CheckIndex
#undef CheckInsertIndex
#undef InitArrayList
代码注释明确,写法统一,也没有什么好说的,依赖标准库的函数。Array的定义,在上篇文章里有说。下面还有一套宏定义的快捷操作。
/** The type is the ArrayList element type */
#define ArrayList(type) ArrayList
/**
* The type is element type
*/
#define AArrayList_GetData(arrayList, type) \
(type*) ((arrayList)->array->data)
/**
* Instead of AArrayList->get for quick iterate element
* return element
*/
#define AArrayList_Get(arrayList, index, type) \
(AArrayList_GetData(arrayList, type))[index]
/**
* Instead of AArrayList->get for quick iterate element
* return element ptr
*/
#define AArrayList_GetPtr(arrayList, index, type) \
(AArrayList_GetData(arrayList, type) + index)
/**
* Instead of AArrayList->set for quick set element
*/
#define AArrayList_Set(arrayList, index, element, type) \
AArrayList_Get(arrayList, index, type) = element
/**
* Init constant ArrayList
*/
#define AArayList_Init(type, increment) \
{ \
increment, \
{ \
NULL, \
0, \
}, \
sizeof(type), \
0, \
}
/**
* Init constant ArrayList with fixed capacity, unable to expansion capacity
*/
#define AArayList_InitFixed(type, capacity, size, ...) \
{ \
0, \
AArray_Init(type, capacity, __VA_ARGS__), \
sizeof(type), \
size, \
}
这里可以看到,我的一个写法。数据属性和数据操作的方法,我分为两个结构体来承载,并且使用单例容器来存放方法。我使用了一套面向对象的C编程的写法。以后会单独介绍。