数据结构
最近研究NSNotification
源码碰上了一个GSIMap
结构体,发现其结构很有意思,即
* A rough picture is include below:
*
*
* This is the map C - array of the buckets
* +---------------+ +---------------+
* | _GSIMapTable | /----->| nodeCount |
* |---------------| / | firstNode ----+--\
* | buckets ---+----/ | .......... | |
* | bucketCount =| size of --> | nodeCount | |
* | nodeChunks ---+--\ | firstNode | |
* | chunkCount =-+\ | | . | |
* | .... || | | . | |
* +---------------+| | | nodeCount | |
* | | | fistNode | |
* / | +---------------+ |
* ---------- v v
* / +----------+ +---------------------------+
* | | * ------+----->| Node1 | Node2 | Node3 ... | a chunk
* chunkCount | * ------+--\ +---------------------------+
* is size of = | . | \ +-------------------------------+
* | . | ->| Node n | Node n + 1 | ... | another
* +----------+ +-------------------------------+
* array pointing
* to the chunks
结构定义如下:
#if !defined(GSI_MAP_TABLE_T)
typedef struct _GSIMapBucket GSIMapBucket_t;
typedef struct _GSIMapNode GSIMapNode_t;
typedef GSIMapBucket_t *GSIMapBucket;
typedef GSIMapNode_t *GSIMapNode;
#endif
struct _GSIMapNode {
GSIMapNode nextInBucket; /* Linked list of bucket. */
GSIMapKey key;
#if GSI_MAP_HAS_VALUE
GSIMapVal value;
#endif
};
struct _GSIMapBucket {
uintptr_t nodeCount; /* Number of nodes in bucket. */
GSIMapNode firstNode; /* The linked list of nodes. */
};
#if defined(GSI_MAP_TABLE_T)
typedef GSI_MAP_TABLE_T *GSIMapTable;
#else
typedef struct _GSIMapTable GSIMapTable_t;
typedef GSIMapTable_t *GSIMapTable;
struct _GSIMapTable {
NSZone *zone;
uintptr_t nodeCount; /* Number of used nodes in map. */
uintptr_t bucketCount; /* Number of buckets in map. */
GSIMapBucket buckets; /* Array of buckets. */
GSIMapNode freeNodes; /* List of unused nodes. */
uintptr_t chunkCount; /* Number of chunks in array. */
GSIMapNode *nodeChunks; /* Chunks of allocated memory. */
uintptr_t increment;
#ifdef GSI_MAP_EXTRA
GSI_MAP_EXTRA extra;
#endif
};
解析
可以从两种角度去解析这个数据结构:
- 从
bucket
的角度,看成一个单向链表
buckets
是一个单向链表,存储着GSIMapBucket
,GSIMapBucket
中其firstNode->nextInBucket
表示下一个bucket
,firstNode
表示另一条单链表的首个元素。bucketCount
表示buckets
的数量
- 从
chunk
角度,看成一个数组指针
nodeChunks
表示一个数组指针,数组存储所有单链表的首个元素node
chunkCount
表示数组大小
此外freeNodes
则就是需要释放的元素,是一个单向链表。
其实就是一个hash
表结构,既可以以数组
的形式取到每个单向链表首元素,也可以以链表形式取得。
通过数组能够方便取到每个单向链表,再利用链表结构方便增删。
//取单向链表
//通过hash值%最大个数 来获取index,然后取出单向链表,再进行链表增删
GS_STATIC_INLINE GSIMapBucket
GSIMapPickBucket(unsigned hash, GSIMapBucket buckets, uintptr_t bucketCount)
{
return buckets + hash % bucketCount;
}
GS_STATIC_INLINE GSIMapBucket
GSIMapBucketForKey(GSIMapTable map, GSIMapKey key)
{
return GSIMapPickBucket(GSI_MAP_HASH(map, key),
map->buckets, map->bucketCount);
}
//增删元素
GS_STATIC_INLINE void
GSIMapLinkNodeIntoBucket(GSIMapBucket bucket, GSIMapNode node)
{
node->nextInBucket = bucket->firstNode;
bucket->firstNode = node;
}
GS_STATIC_INLINE void
GSIMapUnlinkNodeFromBucket(GSIMapBucket bucket, GSIMapNode node)
{
if (node == bucket->firstNode)
{
bucket->firstNode = node->nextInBucket;
}
else
{
GSIMapNode tmp = bucket->firstNode;
while (tmp->nextInBucket != node)
{
tmp = tmp->nextInBucket;
}
tmp->nextInBucket = node->nextInBucket;
}
node->nextInBucket = 0;
}
这是OC
中实现hash
表的一种形式。但对于Java
的hashmap
来说,更为简单了。hashmap
涉及红黑树等查找问题,后续贴出对比下。