给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数
函数f (key)常用的对应关系:
由于C语言中,并没有对hash表这类的高级数据结构进行支持,即使在目前通用的C++中,也只支持栈、队列等几个数据结构,对于map,其实是以树结构来实现的,而不是以hash表实现。
uthash是一个C语言的hash表实现。它以宏定义的方式实现hash表,不仅加快了运行的速度,而且与关键类型无关的优点。
uthash使用起来十分方便,只要将头文件uthash.h包含进去就可以使用。
目前,uthash的最新版本(1.9)支持如下平台:
通常这足够通用了。唯一的不足是在windows下,在uthash这旧版本中,并不支持vs2008和2010,而是支持MinGW。
uthash支持:增加、查找、删除、计数、迭代、排序、选择等操作。
#include "uthash.h"
typedef struct g_hashElement
{
int key;
char name[10];
UT_hash_handle hh;
} tTestHashElement;
tTestHashElement *element = nullptr;
// 添加
void add_user(int userID,char*name)
{
tTestHashElement*starget = (tTestHashElement*)malloc(sizeof(tTestHashElement));
starget->key = userID;
strcpy(starget->name, name);
HASH_ADD_INT(element, key, starget);/* id: name of key field */
}
//查找
tTestHashElement* findUser(int userID)
{
tTestHashElement* sTarget = nullptr;
HASH_FIND_INT(element, &userID, sTarget);
return sTarget;
}
//删除
void deleteUser(tTestHashElement*User)
{
if (!User) {
return;
}
HASH_DEL(element, User);
free(User);
}
//计数
void printHash()
{
unsigned int num_user;
num_user = HASH_COUNT(element);
printf("there are %u element\n",num_user);
tTestHashElement*sww = element;
for (; sww!=nullptr; sww = (tTestHashElement*)sww->hh.next) {
printf("element key %d:name %s\n",sww->key,sww->name);
}
}
//比较
int name_sort(tTestHashElement*a,tTestHashElement*b)
{
return strcmp(a->name, b->name);
}
int id_sort(tTestHashElement*a,tTestHashElement*b)
{
return a->key - b->key;
}
//排序
void sortByName(tTestHashElement*a,tTestHashElement*b)
{
HASH_SORT(element, name_sort);
}
void sortByID(tTestHashElement*a,tTestHashElement*b)
{
HASH_SORT(element, id_sort);
}
在Cocos2d-x里面有几处使用的
复杂的用法就可以参考Cocos2d-x ActionManager 的应用了
此处贴出cpp源码;
#include "CCActionManager.h"
#include "CCNode.h"
#include "CCScheduler.h"
#include "ccMacros.h"
#include "ccCArray.h"
#include "uthash.h"
NS_CC_BEGIN
//
// singleton stuff
//
typedef struct _hashElement
{
struct _ccArray *actions;//动作列表
Node *target;//动作目标
int actionIndex;//动作id
Action *currentAction;//当前动作
bool currentActionSalvaged;//当前动画是否被回收。
bool paused;//是否暂停
UT_hash_handle hh;//哈希表查询句柄。
} tHashElement;
ActionManager::ActionManager(void)
: _targets(nullptr),
_currentTarget(nullptr),
_currentTargetSalvaged(false)
{
}
ActionManager::~ActionManager(void)
{
CCLOGINFO("deallocing ActionManager: %p", this);
//清空所有的action
removeAllActions();
}
// private
//删除相应的哈希表项
void ActionManager::deleteHashElement(tHashElement *element)
{
//释放pElement存储的动画集
ccArrayFree(element->actions);
//从哈希表m_pTargets中删除pElement项
HASH_DEL(_targets, element);
//释放element
element->target->release();
free(element);
}
//为哈希表项pElement申请内存,以存放动画集
void ActionManager::actionAllocWithHashElement(tHashElement *element)
{
// 默认每个哈希表项存四个动画,所以如果当前哈希表项的动画集为空,
// 则为其申请可存放4个动画指针的内存大小。
if (element->actions == nullptr)
{
element->actions = ccArrayNew(4);
}else
if (element->actions->num == element->actions->max)
{//如果哈希表项的动作集需要扩大,则扩增为原来的2倍直至达到最大容量。
ccArrayDoubleCapacity(element->actions);
}
}
//通过下标移除动作
void ActionManager::removeActionAtIndex(ssize_t index, tHashElement *element)
{
Action *action = (Action*)element->actions->arr[index];//索引到action
//若移除的时当前运行的动作 先保留 下移循环再移除
if (action == element->currentAction && (! element->currentActionSalvaged))
{
element->currentAction->retain();
element->currentActionSalvaged = true;
}
//从动作列表移除这个动作
ccArrayRemoveObjectAtIndex(element->actions, index, true);
// update actionIndex in case we are in tick. looping over the actions
//更新动作下标 前移
if (element->actionIndex >= index)
{
element->actionIndex--;
}
//哈希表内没有动作了 若是当前哈希表 暂时不删除这个表 标记下
//若不是这个当前表 直接删除这个哈希表
if (element->actions->num == 0)
{
if (_currentTarget == element)
{
_currentTargetSalvaged = true;
}
else
{
deleteHashElement(element);
}
}
}
// pause / resume
void ActionManager::pauseTarget(Node *target)
{
tHashElement *element = nullptr;
HASH_FIND_PTR(_targets, &target, element);
if (element)
{
element->paused = true;
}
}
void ActionManager::resumeTarget(Node *target)
{
tHashElement *element = nullptr;
HASH_FIND_PTR(_targets, &target, element);
if (element)
{
element->paused = false;
}
}
Vector ActionManager::pauseAllRunningActions()
{
Vector idsWithActions;
for (tHashElement *element=_targets; element != nullptr; element = (tHashElement *)element->hh.next)
{
if (! element->paused)
{
element->paused = true;
idsWithActions.pushBack(element->target);
}
}
return idsWithActions;
}
void ActionManager::resumeTargets(const Vector& targetsToResume)
{
for(const auto &node : targetsToResume) {
this->resumeTarget(node);
}
}
// run
void ActionManager::addAction(Action *action, Node *target, bool paused)
{//指针作为参数 断言是个好习惯
CCASSERT(action != nullptr, "");
CCASSERT(target != nullptr, "");
tHashElement *element = nullptr;/* zero fill! */
// we should convert it to Ref*, because we save it as Ref*
Ref *tmp = target;
HASH_FIND_PTR(_targets, &tmp, element);//通过target 找到对应的哈希表项返回给pElement;
if (! element)
{//如果找不到。则node对应的哈希表还不存在 则申请内存创建此哈希表项,并将其加入哈希表中。
//创建一个哈希表项
element = (tHashElement*)calloc(sizeof(*element), 1);
//设置暂停状态
element->paused = paused;
//引用计数器加1,代表被管理器使用中。
target->retain();
//设置哈希表项中的演员为参数指定的演员
element->target = target;
HASH_ADD_PTR(_targets, target, element);//将哈希表项添加到哈希表
}
//为哈希表申请内存 用来存放action
actionAllocWithHashElement(element);
//动作已经在动作列表 中断 //判断pAction是否在pElement的动画集中。确保只放入一次。
CCASSERT(! ccArrayContainsObject(element->actions, action), "");
ccArrayAppendObject(element->actions, action);//添加到动作列表
//动作绑定目标 //设置是哪个CCNode要进行当前动画
action->startWithTarget(target);
}
// remove
void ActionManager::removeAllActions()
{
for (tHashElement *element = _targets; element != nullptr; )
{//遍历哈希表
auto target = element->target;//得到演员
element = (tHashElement*)element->hh.next;//哈希表项后移
removeAllActionsFromTarget(target);//移除target上的所有action
}
}
void ActionManager::removeAllActionsFromTarget(Node *target)
{
// explicit null handling
if (target == nullptr)
{// 有效性判断
return;
}
//定义一个哈希表项指针,并通过哈希表的查询宏取得对应项地址返回给指针。
tHashElement *element = nullptr;
HASH_FIND_PTR(_targets, &target, element);
if (element) //如果找到此项
{ //如果此哈希表项有正在播放的动画并且这个动画并未被设为要回收。
if (ccArrayContainsObject(element->actions, element->currentAction) && (! element->currentActionSalvaged))
{//将当前正在播放的动画的引用计数器加1并设置其回收标记为true。
//引用计数加1的目的是人为使其暂不能被正常释放,需要待后面再减1后才可以被释放。
element->currentAction->retain();
element->currentActionSalvaged = true;
}
//清空当前哈希表项的动画集。
ccArrayRemoveAllObjects(element->actions);
if (_currentTarget == element)
{ //如果当前哈希表项正处于使用中,暂不释放,只将其要回收的标记设为true。
_currentTargetSalvaged = true;
}
else
{//如果当前哈希表项没有处于使用中 释放当前哈希表项
deleteHashElement(element);
}
}
else
{
// CCLOG("cocos2d: removeAllActionsFromTarget: Target not found");
}
}
void ActionManager::removeAction(Action *action)
{
// explicit null handling
if (action == nullptr)
{//有效性判断
return;
}
//定义一个哈希表项指针,并通过哈希表的查询宏取得对应项地址返回给指针。
tHashElement *element = nullptr;
Ref *target = action->getOriginalTarget();
HASH_FIND_PTR(_targets, &target, element);
if (element)
{//如果找到此项 //取得pAction处于当前项的动画集的索引
auto i = ccArrayGetIndexOfObject(element->actions, action);
if (i != CC_INVALID_INDEX)
{ //如果这个索引是有效的,调用函数将pElement的指定索引的动画移除。
removeActionAtIndex(i, element);
}
}
else
{
CCLOG("cocos2d: removeAction: Target not found");
}
}
//将指定演员的指定动画删除
void ActionManager::removeActionByTag(int tag, Node *target)
{ //有效性判断
CCASSERT(tag != Action::INVALID_TAG, "");
CCASSERT(target != nullptr, "");
//定义哈希表项指针变量,并通过哈希表处理宏取得相应哈希表项
tHashElement *element = nullptr;
HASH_FIND_PTR(_targets, &target, element);
//如果能找到哈希表项
if (element)
{ //取得哈希表项的动画集中的动画数量
auto limit = element->actions->num;
for (int i = 0; i < limit; ++i)
{//遍历动画集中的所有动画 //取得每一项动画
Action *action = (Action*)element->actions->arr[i];
//查看是否是指定演员的指定动画。
if (action->getTag() == (int)tag && action->getOriginalTarget() == target)
{ //如果是,则删除此项。
removeActionAtIndex(i, element);
break;
}
}
}
}
// get
//取得指定演员的指定动画
// XXX: Passing "const O *" instead of "const O&" because HASH_FIND_IT requries the address of a pointer
// and, it is not possible to get the address of a reference
Action* ActionManager::getActionByTag(int tag, const Node *target) const
{//断言 有效性判断
CCASSERT(tag != Action::INVALID_TAG, "");
//定义哈希表项指针变量
tHashElement *element = nullptr;
HASH_FIND_PTR(_targets, &target, element);//查找哈希表
//如果能找到哈希表项
if (element)
{ //如果此哈希表项的动画集不为空
if (element->actions != nullptr)
{ //取得哈希表项的动画集中的动画数量
auto limit = element->actions->num;
for (int i = 0; i < limit; ++i)
{//遍历动画集中的所有动画 //取得每一项动画
Action *action = (Action*)element->actions->arr[i];
//查看是否是指定动画。
if (action->getTag() == (int)tag)
{
return action;
}
}
}
CCLOG("cocos2d : getActionByTag(tag = %d): Action not found", tag);
}
else
{
// CCLOG("cocos2d : getActionByTag: Target not found");
}
//找不到要找的动画。返回NULL。
return nullptr;
}
// XXX: Passing "const O *" instead of "const O&" because HASH_FIND_IT requries the address of a pointer //取得指定演员的动画集中的动画数量
// and, it is not possible to get the address of a reference
ssize_t ActionManager::getNumberOfRunningActionsInTarget(const Node *target) const
{//定义哈希表项指针变量,并通过哈希表处理宏取得相应哈希表项
tHashElement *element = nullptr;
HASH_FIND_PTR(_targets, &target, element);
if (element)
{ //如果找到了,判断动画集是否为空,如果不为空返回动画数量,否则返回零。
return element->actions ? element->actions->num : 0;
}
return 0;
}
// main loop //动画管理器的更新函数
void ActionManager::update(float dt)
{//遍历哈希表的所有项
for (tHashElement *elt = _targets; elt != nullptr; )
{ //将当前哈希表项保存到变量m_pCurrentTarget中。并将此项对应的回收标记m_bCurrentTargetSalvaged 设为false,
_currentTarget = elt;
_currentTargetSalvaged = false;
//当前动作没有暂停
if (! _currentTarget->paused)
{//遍历当前项的动画集中的所有动画
// The 'actions' MutableArray may change while inside this loop.
for (_currentTarget->actionIndex = 0; _currentTarget->actionIndex < _currentTarget->actions->num;
_currentTarget->actionIndex++)
{//取得遍历项的动画
_currentTarget->currentAction = (Action*)_currentTarget->actions->arr[_currentTarget->actionIndex];
if (_currentTarget->currentAction == nullptr)
{//如果遍历项动画为空,跳过后遍历下一个动画。
continue;
}
//设置回收标记为false。
_currentTarget->currentActionSalvaged = false;
//更新当前动画的播放
_currentTarget->currentAction->step(dt);
if (_currentTarget->currentActionSalvaged)
{ //如果当前项的回收标记为true,则进行释放处理。
// The currentAction told the node to remove it. To prevent the action from
// accidentally deallocating itself before finishing its step, we retained
// it. Now that step is done, it's safe to release it.
_currentTarget->currentAction->release();
} else
if (_currentTarget->currentAction->isDone())
{ //如果当前动画处于结束状态,则停止动画
_currentTarget->currentAction->stop();
//为了在removeAction中正确释放动画,这里先创建一个临时变量pAction记录一下要释放的动画。
Action *action = _currentTarget->currentAction;
// Make currentAction nil to prevent removeAction from salvaging it.
//在removeAction之前将当前哈希表项中的当前动画设为NULL,否则不能释放。
_currentTarget->currentAction = nullptr;
removeAction(action);
}
_currentTarget->currentAction = nullptr;
}
}
//使for循环能够继续
// elt, at this moment, is still valid
// so it is safe to ask this here (issue #490)
elt = (tHashElement*)(elt->hh.next);
// 如果当前哈希表项处于回收状态且其动画集为空,删除此哈希表项。
// only delete currentTarget if no actions were scheduled during the cycle (issue #481)
if (_currentTargetSalvaged && _currentTarget->actions->num == 0)
{
deleteHashElement(_currentTarget);
}
}
// issue #635
_currentTarget = nullptr;
}
NS_CC_END