C 实现ArrayMap字典映射

来源:互联网 发布:网络大电影拍摄团队 编辑:程序博客网 时间:2024/06/05 17:09

    字典映射是最重要的基础工具。我并没有使用hash做散列映射,而是使用了简单直接的二分查找。其主要的思路如下:

  • 使用字符串的长度,进行二分查找存储
  • 如果字符长度相等,使用字符串的字典顺序,进行二分查找存储
  • 缓存字符串的长度,用来减少字符串长度的计算函数调用
  • 元素是有序的存储在一个动态数组里
  • 提供一套元素的访问方法
  • 实现依赖 C 实现泛型ArrayList数组
     首先看结构
     
typedef struct{const char*  key;/** strlen(key) + 1 include '\0' */int          keyLength;/** * ArrayStrMap value pointer * value data copy into ArrayStrMapElement */void*        valuePtr;}ArrayStrMapElement;typedef struct{ArrayList arrayList[1];/** ArrayStrMap value type sizeof */int typeSize;}ArrayStrMap;

      ArrayStrMapElement 代表了存储的一个元素,valuePtr 指向传入存储的元素结构,可以看到缓存了key和key length。ArrayStrMap是用了ArrayList来存储数据,每个数据就是一个ArrayStrMapElement,typeSize是valuePtr指向元素的结构大小。

     
      一系列元素访问接口如下:
     
typedef struct{ArrayStrMap* (*Create) (int typeSize);void         (*Init)   (int typeSize, ArrayStrMap* outArrayStrMap);ArrayStrMap* (*CreateWithCapacity) (int typeSize, int capacity);void         (*InitWithCapacity)   (int typeSize, int capacity, ArrayStrMap* outArrayStrMap);/** * Release member memory space */void         (*Release)           (ArrayStrMap*  arrayStrMap);/** * Put key and value from valuePtr into ArrayStrMap * valuePtr point to value * return valuePtr in ArrayStrMap */void*        (*Put)               (ArrayStrMap* arrayStrMap, const char* key, void* valuePtr);/** * Get valuePtr by key, if no key found return defaultValuePtr */void*        (*Get)               (ArrayStrMap* arrayStrMap, const char* key, void* defaultValuePtr);/** * Set new value from valuePtr by key , return valuePtr in ArrayStrMap */void*        (*Set)               (ArrayStrMap* arrayStrMap, const char* key, void* valuePtr);/** * Remove value by key     * return true success, false failed */bool         (*TryRemove)         (ArrayStrMap* arrayStrMap, const char* key);/** * Clear all value, reset size 0, and keep memory space */void         (*Clear)             (ArrayStrMap* arrayStrMap);/** * Insert value from valuePtr at index, the possible index may be -getIndex() - 1 * return valuePtr in ArrayStrMap */void*        (*InsertAt)          (ArrayStrMap* arrayStrMap, const char* key, int index, void* valuePtr);/** * Get index of key, if negative not found then return -insertIndex - 1 * so insert index is -getIndex() - 1 */int          (*GetIndex)          (ArrayStrMap* arrayStrMap, const char* key);/** * Get key at index */const char*  (*GetKey)            (ArrayStrMap* arrayStrMap, int index);/** * Get valuePtr at index */void*        (*GetAt)             (ArrayStrMap* arrayStrMap, int index);/** * Put value from valuePtr at index * return valuePtr in ArrayStrMap */void*        (*PutAt)             (ArrayStrMap* arrayStrMap, int index, void* valuePtr);/** * Remove value at index */void         (*RemoveAt)          (ArrayStrMap* arrayStrMap, int index);}_AArrayStrMap_;extern _AArrayStrMap_ AArrayStrMap[1];


    实现代码如下:
#define CheckIndex(tag, index, elements) \ALog_A(index >= 0 && index < elements->size, "ArrayStrMap "  tag " index = %d, size = %d, invalid", index, elements->size)#define CheckInsertIndex(tag, index, elements) \ALog_A(index >= 0 && index <= elements->size, "ArrayStrMap " tag " index = %d, size = %d, invalid", index, elements->size)#define BinarySearch(key) \ArrayList* elements  = arrayStrMap->arrayList;                                                        \int        keyLength = (int) strlen(key) + 1;                                                         \int        high      = elements->size;                                                                \int        low       = -1;                                                                            \int        guess     = -1;                                                                            \int        cmp       = -1;                                                                            \                                                                                                      \while (high - low > 1)                                                                                \    {                                                                                                     \guess                                = (high + low) >> 1;  /** not consider integer overflow */   \ArrayStrMapElement* element          = AArrayList_Get(elements, guess, ArrayStrMapElement*);      \int                 elementKeyLength = element->keyLength;                                        \  \if (elementKeyLength < keyLength)   \        {                                                                                                 \low  = guess;                                                                                 \}                                                                                                 \        else if (elementKeyLength > keyLength)                                                            \        {                                                                                                 \high = guess;                                                                                 \}           \        else if (elementKeyLength == keyLength)  \        {                                                         \cmp  = memcmp(element->key, key, keyLength);                                                  \if (cmp < 0)   \            {                                                                                  \low  = guess;                                                                             \}      \else if (cmp > 0)  \{                                                                          \high = guess;                                                                             \}       \else if (cmp == 0)       \{                                                                         \/** cmp 0 means find the key */                                                           \break;                                                                                    \}                                                                                             \}                                                                                                 \ }// -----------------------------------------------------------------------------------------static void* Put(ArrayStrMap* arrayStrMap, const char* key, void* valuePtr){BinarySearch(key);ALog_A(cmp != 0, "ArrayStrMap put key = %s, has already exist", key);int                 typeSize = arrayStrMap->typeSize;ArrayStrMapElement* element  = (ArrayStrMapElement*)        malloc(sizeof(ArrayStrMapElement) + typeSize + keyLength);element->keyLength           = keyLength;element->valuePtr            = (char*) element + sizeof(ArrayStrMapElement);memcpy(element->valuePtr, valuePtr, typeSize);element->key                 = (char*) element->valuePtr + typeSize;memcpy((void*) element->key, key, keyLength);// if guess == high find guess is bigger than key in ArrayStrMap insert value hereif (guess == low){// find guess is smaller than key in ArrayStrMap insert value after// or ArrayStrMap emptyguess++;}AArrayList->Insert(elements, guess, &element);    return element->valuePtr;}static void* Get(ArrayStrMap* arrayStrMap, const char* key, void* defaultValuePtr){BinarySearch(key);return cmp == 0 ? AArrayList_Get(elements, guess, ArrayStrMapElement*)->valuePtr : defaultValuePtr;}static void* Set(ArrayStrMap* arrayStrMap, const char* key, void* valuePtr){BinarySearch(key);ALog_A(cmp == 0, "ArrayStrMap set key = %s, has not exist", key);void* elementValuePtr = AArrayList_Get(elements, guess, ArrayStrMapElement*)->valuePtr;memcpy(elementValuePtr, valuePtr, arrayStrMap->typeSize);return elementValuePtr;}static bool TryRemove(ArrayStrMap* arrayStrMap, const char* key){BinarySearch(key);if (cmp == 0){free(AArrayList_Get(elements, guess, ArrayStrMapElement*));AArrayList->Remove(elements, guess);return true;}return false;}static void Clear(ArrayStrMap* arrayStrMap){ArrayList* elements = arrayStrMap->arrayList;for (int i = 0; i < elements->size; i++){free(AArrayList_Get(elements, i, ArrayStrMapElement*));}AArrayList->Clear(elements);}static void* InsertAt(ArrayStrMap* arrayStrMap, const char* key, int index, void* valuePtr){ArrayList* elements = arrayStrMap->arrayList;CheckInsertIndex("InsertAt", index, elements);int keyLength = (int) strlen(key) + 1;int typeSize  = arrayStrMap->typeSize;ArrayStrMapElement* element = (ArrayStrMapElement*)  malloc(sizeof(ArrayStrMapElement) + typeSize + keyLength);element->keyLength          = keyLength;element->valuePtr           = (char*) element + sizeof(ArrayStrMapElement);memcpy(element->valuePtr, valuePtr, typeSize);element->key                = (char*) element->valuePtr + typeSize;memcpy((void*) element->key, key, keyLength);AArrayList->Insert(elements, index, &element);return element->valuePtr;}static int GetIndex(ArrayStrMap* arrayStrMap, const char* key){BinarySearch(key);if (cmp == 0){return guess;}if (guess == low){// find guess is smaller than key in ArrayStrMap or ArrayStrMap emptyguess++;}// when ArrayStrMap empty guess is 0, so we -1return -guess - 1;}static const char* GetKey(ArrayStrMap* arrayStrMap, int index){ArrayList* elements = arrayStrMap->arrayList;CheckIndex("GetKey", index, elements);return AArrayList_Get(elements, index, ArrayStrMapElement*)->key;}static void* GetAt(ArrayStrMap* arrayStrMap, int index){ArrayList* elements = arrayStrMap->arrayList;CheckIndex("GetAt", index, elements);return AArrayList_Get(elements, index, ArrayStrMapElement*)->valuePtr;}static void* PutAt(ArrayStrMap* arrayStrMap, int index, void* valuePtr){ArrayList* elements = arrayStrMap->arrayList;CheckIndex("PutAt", index, elements);ArrayStrMapElement* element = AArrayList_Get(elements, index, ArrayStrMapElement*);memcpy(element->valuePtr, valuePtr, arrayStrMap->typeSize);return element->valuePtr;}static void RemoveAt(ArrayStrMap* arrayStrMap, int index){ArrayList* elements = arrayStrMap->arrayList;CheckIndex("RemoveAt", index, elements);free(AArrayList_Get(elements, index, ArrayStrMapElement*));AArrayList->Remove(elements, index);}static void Release(ArrayStrMap*  arrayStrMap){ArrayList* elements = arrayStrMap->arrayList;for (int i = 0; i < elements->size; i++){free(AArrayList_Get(elements, i, ArrayStrMapElement*));}AArrayList->Release(elements);}static void InitWithCapacity(int typeSize, int capacity, ArrayStrMap* outArrayStrMap){if (capacity == 0){AArrayList->Init(sizeof(ArrayStrMapElement*), outArrayStrMap->arrayList);}else{AArrayList->InitWithCapacity(sizeof(ArrayStrMapElement*), capacity, outArrayStrMap->arrayList);}outArrayStrMap->typeSize = typeSize;}static ArrayStrMap* CreateWithCapacity(int typeSize, int capacity){ArrayStrMap* arrayStrMap = (ArrayStrMap*) malloc(sizeof(ArrayStrMap));InitWithCapacity(typeSize, capacity, arrayStrMap);return arrayStrMap;}static void Init(int typeSize, ArrayStrMap* outArrayStrMap){InitWithCapacity(typeSize, 0, outArrayStrMap);}static ArrayStrMap* Create(int typeSize){return CreateWithCapacity(typeSize, 0);}_AArrayStrMap_ AArrayStrMap[1] ={Create,Init,CreateWithCapacity,InitWithCapacity,Release,Put,Get,Set,TryRemove,Clear,InsertAt,GetIndex,GetKey,GetAt,PutAt,RemoveAt,};#undef CheckIndex#undef BinarySearch#undef CheckInsertIndex


    同样会提供宏定义的快捷操作:
    
/** The type is the ArrayStrMap value type */#define ArrayStrMap(valueType) ArrayStrMap/** * Get key in ArrayStrMapElement by valuePtr with typeSize */#define AArrayStrMap_GetKey(valuePtr, typeSize) \((const char*) ((char*) valuePtr + typeSize))/** * Init constant ArrayStrMap, as element in ArrayStrmap array * use like ArrayStrmap arr[1] = AArrayStrMap_Init(type, increment) */#define AArrayStrMap_Init(type, increment) \{                                                       \{                                                   \AArayList_Init(ArrayStrMapElement*, increment), \sizeof(type),                                   \  }                                                  \}


    在实际的使用中,效率不错。虽然没有hash那样的常数级的查找,但hash会有冲突的问题,以及计算hash的运算。二分查找,50个元素需要1 - 6次查找,100个元素需要 1 - 7次查询,1000个元素需要 1 - 10次查找,10000个元素需要1 - 14次查找。通常情况下都是50个元素以内的查找。

1 0
原创粉丝点击