hashmap

Posted renhl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hashmap相关的知识,希望对你有一定的参考价值。

 

hashmap.h

#ifndef foohashmaphfoo
#define foohashmaphfoo

/* $Id: hashmap.h 90 2004-07-17 14:12:30Z lennart $ */

/***
  This file is part of polypaudio.
 
  polypaudio is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published
  by the Free Software Foundation; either version 2 of the License,
  or (at your option) any later version.
 
  polypaudio is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  General Public License for more details.
 
  You should have received a copy of the GNU General Public License
  along with polypaudio; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  USA.
***/

struct pa_hashmap;

struct pa_hashmap *pa_hashmap_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b));
void pa_hashmap_free(struct pa_hashmap*, void (*free_func)(void *p, void *userdata), void *userdata);

int pa_hashmap_put(struct pa_hashmap *h, const void *key, void *value);
void* pa_hashmap_get(struct pa_hashmap *h, const void *key);

int pa_hashmap_remove(struct pa_hashmap *h, const void *key);

unsigned pa_hashmap_ncontents(struct pa_hashmap *h);

#endif

 

hashmap.c

/* $Id: hashmap.c 90 2004-07-17 14:12:30Z lennart $ */

/***
  This file is part of polypaudio.
 
  polypaudio is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published
  by the Free Software Foundation; either version 2 of the License,
  or (at your option) any later version.
 
  polypaudio is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  General Public License for more details.
 
  You should have received a copy of the GNU General Public License
  along with polypaudio; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  USA.
***/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdlib.h>
#include <assert.h>
#include <string.h>

#include "hashmap.h"
#include "idxset.h"

struct hashmap_entry {
    //bucket_next, bucket_previous用于data数组,连接相邻的两个元素;next,previous用于first_entry的链表中两个相邻元素的连接
    struct hashmap_entry *next, *previous, *bucket_next, *bucket_previous;
    //hash 值
    unsigned hash;
    //key
    const void *key;
    //key 对应的data
   void *value;
};

struct pa_hashmap {
     //data数组的大小,固定为1023
     unsigned size;
    //index是由key进行hash后,对1023取余得到的
    //将对应的 hashmap_entry *存入data[index]指定的链表
    struct hashmap_entry **data;
    //对所有的元素进行存入链表中
   struct hashmap_entry *first_entry;
    
    //hashmap_entry元素的个数
    unsigned n_entries;
    //对key取hash的函数
   unsigned (*hash_func) (const void *p);
    //key的比较函数
   int (*compare_func) (const void*a, const void*b);
};

//初始化pa_hashmap
struct pa_hashmap *pa_hashmap_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)) {
    struct pa_hashmap *h;
    h = malloc(sizeof(struct pa_hashmap));
    assert(h);
    //data分配一个1023大小的数组,每个元素是hashmap_entry*
    h->data = malloc(sizeof(struct hashmap_entry*)*(h->size = 1023));
    assert(h->data);
    memset(h->data, 0, sizeof(struct hashmap_entry*)*(h->size = 1023));
    h->first_entry = NULL;
    h->n_entries = 0;
    h->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func;
    h->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func;
    return h;
}

//将h中的e删除
//从两个双向链表中删除;删除元素本身;个数减1
static void remove(struct pa_hashmap *h, struct hashmap_entry *e) {
    assert(e);
   
    //e->next为非空,表示不是链表的最后一个元素
    if (e->next)
        e->next->previous = e->previous;
    //e->previous为非空,表示不是链表的第一个元素
    if (e->previous)
        e->previous->next = e->next;
    else
        h->first_entry = e->next;

    //e->bucket_next为非空,表示不是data[index]指向的链表的最后一个元素
    if (e->bucket_next)
        e->bucket_next->bucket_previous = e->bucket_previous;
    //e->bucket_previous为非空,表示不是data[index]指向的链表的第一个元素
    if (e->bucket_previous)
        e->bucket_previous->bucket_next = e->bucket_next;
    else
        h->data[e->hash] = e->bucket_next;

    free(e);
    //个数减1
    h->n_entries--;
}

//释放h
//h->first_entry的链表指向所有的元素,对每个元素进行删除;释放data数组;释放h
void pa_hashmap_free(struct pa_hashmap*h, void (*free_func)(void *p, void *userdata), void *userdata) {
    assert(h);

    while (h->first_entry) {
        if (free_func)
            free_func(h->first_entry->value, userdata);
        remove(h, h->first_entry);
    }
    
    free(h->data);
    free(h);
}

//在h中取得hash (实际上也是由key得到的)和key指向的hashmap_entry*
//用key得到hashmap_entry*
static struct hashmap_entry *get(struct pa_hashmap *h, unsigned hash, const void *key) {
    struct hashmap_entry *e;
    
    //在h->data[hash]的链表中,遍历查找key元素的hashmap_entry*
    for (e = h->data[hash]; e; e = e->bucket_next)
        if (h->compare_func(e->key, key) == 0)
            return e;

    return NULL;
}

//插入key和value到h中
//检查key是否重复,重复返回-1;不重复,插入到h->first_entry和h->data[hash]中链表的开头;数目加1
int pa_hashmap_put(struct pa_hashmap *h, const void *key, void *value) {
    struct hashmap_entry *e;
    unsigned hash;
    assert(h && key);
    //计算hash
    hash = h->hash_func(key) % h->size;
    //用hash和key获取hashmap_entry * 
    if ((e = get(h, hash, key)))
        return -1; //如果存在,返回-1表示插入重复key
    
    e = malloc(sizeof(struct hashmap_entry));
    assert(e);
    
    e->hash = hash;
    e->key = key;
    e->value = value;
    
    //插入到h->first_entry指向的双向链表的开头
    e->previous = NULL;
    e->next = h->first_entry;
    if (h->first_entry)
        h->first_entry->previous = e;
    h->first_entry = e;
    
    //插入到h->data[hash]指向的双向链表的开头
    e->bucket_previous = NULL;
    e->bucket_next = h->data[hash];
    if (h->data[hash])
        h->data[hash]->bucket_previous = e;
    h->data[hash] = e;
    
    //数目加1
    h->n_entries ++;
    return 0;
}

//在h中取得key对应的data
//有key计算hash;由hash和key取得hashmap_entry *;返回hashmap_entry *中的value
void* pa_hashmap_get(struct pa_hashmap *h, const void *key) {
    unsigned hash;
    struct hashmap_entry *e;
    assert(h && key);
    //由key计算hash
    hash = h->hash_func(key) % h->size;
    //由key和hash取得hashmap_entry *
    if (!(e = get(h, hash, key)))
        return NULL; //key没有插入到h中

    return e->value; //返回key对应的data
}
//删除key指定的元素
int pa_hashmap_remove(struct pa_hashmap *h, const void *key) {
    struct hashmap_entry *e;
    unsigned hash;
    assert(h && key);
    //由key计算hash
    hash = h->hash_func(key) % h->size;
    //由key和hash取得hashmap_entry *
    if (!(e = get(h, hash, key)))
        return 1;
    //删除h中的e元素
    remove(h, e);
    return 0;
}
//返回元素的个数
unsigned pa_hashmap_ncontents(struct pa_hashmap *h) {
    return h->n_entries;
}

 

以上是关于hashmap的主要内容,如果未能解决你的问题,请参考以下文章

HashMap原理:哈希函数的设计

HashMap深度解析

JDK源码阅读之 HashMap

ArrayList 和 HashMap 的默认大小是多数?

如何将 Parcelable 与 HashMap 一起使用

hashmap冲突的解决方法以及原理分析: