Maison développement back-end tutoriel php php内核解析:PHP中的哈希表_php技巧

php内核解析:PHP中的哈希表_php技巧

May 17, 2016 am 08:50 AM
哈希表

PHP中使用最为频繁的数据类型非字符串和数组莫属,PHP比较容易上手也得益于非常灵活的数组类型。 在开始详细介绍这些数据类型之前有必要介绍一下哈希表(HashTable)。 哈希表是PHP实现中尤为关键的数据结构。

哈希表在实践中使用的非常广泛,例如编译器通常会维护的一个符号表来保存标记,很多高级语言中也显式的支持哈希表。 哈希表通常提供查找(Search),插入(Insert),删除(Delete)等操作,这些操作在最坏的情况下和链表的性能一样为O(n)。 不过通常并不会这么坏,合理设计的哈希算法能有效的避免这类情况,通常哈希表的这些操作时间复杂度为O(1)。 这也是它被钟爱的原因。
正是因为哈希表在使用上的便利性及效率上的表现,目前大部分动态语言的实现中都使用了哈希表。

为了方便读者阅读后面的内容,这里提前列举一下HashTable实现中出现的基本概念。 哈希表是一种通过哈希函数,将特定的键映射到特定值的一种数据结构,它维护键和值之间一一对应关系。
键(key):用于操作数据的标示,例如PHP数组中的索引,或者字符串键等等。

槽(slot/bucket):哈希表中用于保存数据的一个单元,也就是数据真正存放的容器。

哈希函数(hash function):将key映射(map)到数据应该存放的slot所在位置的函数。

哈希冲突(hash collision):哈希函数将两个不同的key映射到同一个索引的情况。

哈希表可以理解为数组的扩展或者关联数组,数组使用数字下标来寻址,如果关键字(key)的范围较小且是数字的话, 我们可以直接使用数组来完成哈希表,而如果关键字范围太大,如果直接使用数组我们需要为所有可能的key申请空间。 很多情况下这是不现实的。即使空间足够,空间利用率也会很低,这并不理想。同时键也可能并不是数字, 在PHP中尤为如此,所以人们使用一种映射函数(哈希函数)来将key映射到特定的域中:

复制代码 代码如下:

h(key) -> index

通过合理设计的哈希函数,我们就能将key映射到合适的范围,因为我们的key空间可以很大(例如字符串key), 在映射到一个较小的空间中时可能会出现两个不同的key映射被到同一个index上的情况, 这就是我们所说的出现了冲突。 目前解决hash冲突的方法主要有两种:链接法和开放寻址法。

冲突解决

链接法:链接法通过使用一个链表来保存slot值的方式来解决冲突,也就是当不同的key映射到一个槽中的时候使用链表来保存这些值。 所以使用链接法是在最坏的情况下,也就是所有的key都映射到同一个槽中了,操作链表的时间复杂度为O(n)。 所以选择一个合适的哈希函数是最为关键的。目前PHP中HashTable的实现就是采用这种方式来解决冲突的。
开放寻址法:通常还有另外一种解决冲突的方法:开放寻址法。使用开放寻址法是槽本身直接存放数据, 在插入数据时如果key所映射到的索引已经有数据了,这说明发生了冲突,这是会寻找下一个槽, 如果该槽也被占用了则继续寻找下一个槽,直到寻找到没有被占用的槽,在查找时也使用同样的策律来进行。

哈希表的实现

在了解到哈希表的原理之后要实现一个哈希表也很容易,主要需要完成的工作只有三点:
实现哈希函数
冲突的解决
操作接口的实现
首先我们需要一个容器来保存我们的哈希表,哈希表需要保存的内容主要是保存进来的的数据, 同时为了方便的得知哈希表中存储的元素个数,需要保存一个大小字段, 第二个需要的就是保存数据的容器了。作为实例,下面将实现一个简易的哈希表。基本的数据结构主要有两个, 一个用于保存哈希表本身,另外一个就是用于实际保存数据的单链表了,定义如下:

复制代码 代码如下:

typedef struct _Bucket
{
 char *key;
 void *value;
 struct _Bucket *next;

} Bucket;

typedef struct _HashTable
{
 int size;
 Bucket* buckets;
} HashTable;

上面的定义和PHP中的实现类似,为了便于理解裁剪了大部分无关的细节,在本节中为了简化, key的数据类型为字符串,而存储的数据类型可以为任意类型。
Bucket结构体是一个单链表,这是为了解决多个key哈希冲突的问题,也就是前面所提到的的链接法。 当多个key映射到同一个index的时候将冲突的元素链接起来。
哈希函数需要尽可能的将不同的key映射到不同的槽(slot或者bucket)中,首先我们采用一种最为简单的哈希算法实现: 将key字符串的所有字符加起来,然后以结果对哈希表的大小取模,这样索引就能落在数组索引的范围之内了。

复制代码 代码如下:

static int hash_str(char *key)
{
 int hash = 0;

 char *cur = key;

 while(*(cur++) != '\0') {
 hash += *cur;
 }

 return hash;
}

// 使用这个宏来求得key在哈希表中的索引
#define HASH_INDEX(ht, key) (hash_str((key)) % (ht)->size)

这个哈希算法比较简单,它的效果并不好,在实际场景下不会使用这种哈希算法, 例如PHP中使用的是称为DJBX33A算法, 这里列举了Mysql,OpenSSL等开源软件使用的哈希算法, 有兴趣的读者可以前往参考。
操作接口的实现
为了操作哈希表,实现了如下几个操作函数:

复制代码 代码如下:

int hash_init(HashTable *ht); // 初始化哈希表
int hash_lookup(HashTable *ht, char *key, void **result); // 根据key查找内容
int hash_insert(HashTable *ht, char *key, void *value); // 将内容插入到哈希表中
int hash_remove(HashTable *ht, char *key); // 删除key所指向的内容
int hash_destroy(HashTable *ht);

下面以插入和获取操作函数为例:

复制代码 代码如下:

int hash_insert(HashTable *ht, char *key, void *value)
 {
 // check if we need to resize the hashtable
 resize_hash_table_if_needed(ht); // 哈希表不固定大小,当插入的内容快占满哈表的存储空间
 // 将对哈希表进行扩容, 以便容纳所有的元素

int index = HASH_INDEX(ht, key); // 找到key所映射到的索引

Bucket *org_bucket = ht->buckets[index];
 Bucket *bucket = (Bucket *)malloc(sizeof(Bucket)); // 为新元素申请空间

bucket->key = strdup(key);
 // 将值内容保存进来, 这里只是简单的将指针指向要存储的内容,而没有将内容复制。
 bucket->value = value;

LOG_MSG("Insert data p: %p\n", value);

ht->elem_num += 1; // 记录一下现在哈希表中的元素个数

if(org_bucket != NULL) { // 发生了碰撞,将新元素放置在链表的头部
 LOG_MSG("Index collision found with org hashtable: %p\n", org_bucket);
 bucket->next = org_bucket;
 }

ht->buckets[index]= bucket;

LOG_MSG("Element inserted at index %i, now we have: %i elements\n",
 index, ht->elem_num);

return SUCCESS;
 }
 

上面这个哈希表的插入操作比较简单,简单的以key做哈希,找到元素应该存储的位置,并检查该位置是否已经有了内容, 如果发生碰撞则将新元素链接到原有元素链表头部。在查找时也按照同样的策略,找到元素所在的位置,如果存在元素, 则将该链表的所有元素的key和要查找的key依次对比, 直到找到一致的元素,否则说明该值没有匹配的内容。

复制代码 代码如下:

int hash_lookup(HashTable *ht, char *key, void **result)
{
 int index = HASH_INDEX(ht, key);
 Bucket *bucket = ht->buckets[index];

 if(bucket == NULL) return FAILED;

 // 查找这个链表以便找到正确的元素,通常这个链表应该是只有一个元素的,也就不用多次
 // 循环。要保证这一点需要有一个合适的哈希算法,见前面相关哈希函数的链接。
 while(bucket)
 {
 if(strcmp(bucket->key, key) == 0)
 {
 LOG_MSG("HashTable found key in index: %i with key: %s value: %p\n",
 index, key, bucket->value);
 *result = bucket->value;
 return SUCCESS;
 }

 bucket = bucket->next;
 }

 LOG_MSG("HashTable lookup missed the key: %s\n", key);
 return FAILED;
}

PHP中数组是基于哈希表实现的,依次给数组添加元素时,元素之间是有先后顺序的,而这里的哈希表在物理位置上显然是接近平均分布的, 这样是无法根据插入的先后顺序获取到这些元素的,在PHP的实现中Bucket结构体还维护了另一个指针字段来维护元素之间的关系。 具体内容在后一小节PHP中的HashTable中进行详细说明。上面的例子就是PHP中实现的一个精简版。

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover

AI Clothes Remover

Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

Video Face Swap

Video Face Swap

Échangez les visages dans n'importe quelle vidéo sans effort grâce à notre outil d'échange de visage AI entièrement gratuit !

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Le principe, la mise en œuvre et les problèmes courants de la table de hachage PHP Le principe, la mise en œuvre et les problèmes courants de la table de hachage PHP May 07, 2024 pm 12:51 PM

Les tables de hachage mappent les clés aux indices du tableau via des fonctions de hachage pour obtenir une recherche, une insertion et une suppression rapides. PHP utilise des tableaux et la fonction de hachage md5() pour implémenter des tables de hachage et résoudre les conflits grâce à l'exploration linéaire. Les problèmes courants incluent les collisions de hachage (peuvent être résolues en augmentant la taille du tableau ou en optimisant la fonction de hachage), les collisions de hachage (peuvent être évitées par des fonctions de hachage sécurisées) et les performances (dépend de la fonction de hachage et de la méthode de résolution des collisions). Cas pratiques tels que le comptage de mots, le comptage rapide des fréquences de mots via des tables de hachage.

Tables de hachage et tables de hachage en C++ Tables de hachage et tables de hachage en C++ Aug 21, 2023 pm 09:58 PM

Tables de hachage et tables de hachage en C++ Les tables de hachage et les tables de hachage sont des structures de données très courantes en informatique. Pourquoi? Parce que les tables de hachage et les tables de hachage peuvent localiser rapidement un élément spécifique en temps constant. Dans de nombreuses applications, cette différence de performances est significative. Alors, quelle est la différence entre une table de hachage et une table de hachage ? En C++, la différence entre les deux est très subtile et ils peuvent généralement être considérés comme le même concept. Juste dans cet article, nous présenterons en détail les tables de hachage et les tables de hachage. Table de hachage Une table de hachage est une table de hachage

Structures de données PHP SPL : une boîte à outils pour donner un nouveau look à votre code Structures de données PHP SPL : une boîte à outils pour donner un nouveau look à votre code Feb 19, 2024 pm 12:09 PM

Structures de données PHPSPL : présentation Les structures de données phpSPL sont un composant de la bibliothèque standard PHP (SPL) qui fournit un ensemble de structures de données communes, notamment des piles, des files d'attente, des tableaux et des tables de hachage. Ces structures de données sont optimisées pour gérer efficacement une variété de types de données et fournir une interface cohérente qui simplifie le développement d'applications. Pile de structure de données principale Une pile est une collection ordonnée suivant le principe du dernier entré, premier sorti (LIFO). Dans la pile, le dernier élément ajouté sera le premier élément supprimé. SPL fournit une classe SplStack pour représenter une pile. L'exemple suivant montre comment utiliser SplStack : $stack=newSplStack();$stack->push(1

Comment gérer les problèmes d'accès simultané aux tables de hachage en langage Go ? Comment gérer les problèmes d'accès simultané aux tables de hachage en langage Go ? Oct 08, 2023 pm 04:42 PM

Comment gérer les problèmes d’accès simultané aux tables de hachage en langage Go ? En langage Go, les données peuvent être stockées et récupérées efficacement à l'aide de tables de hachage. Cependant, l'accès et la modification simultanés des tables de hachage dans plusieurs goroutines simultanées peuvent facilement conduire à des conditions de concurrence critique et à des incohérences de données. La résolution de ces problèmes nécessite l'utilisation de mécanismes de contrôle de concurrence appropriés, tels que les verrous mutex et les verrous en lecture-écriture. Cet article explique comment gérer les problèmes d'accès simultané aux tables de hachage dans le langage Go et fournit des exemples de code correspondants. Utilisez mutex (Mutex) pour assurer la sécurité de la concurrence : mutex est G

Utilisation d'une table de hachage pour implémenter la recherche de chaînes en C++ Utilisation d'une table de hachage pour implémenter la recherche de chaînes en C++ Aug 22, 2023 pm 12:03 PM

Une table de hachage est une structure de données très courante qui mappe les valeurs clés dans une table de taille fixe, permettant des opérations efficaces de recherche, d'insertion et de suppression. En C++, nous pouvons utiliser unordered_map en STL (StandardTemplateLibrary) pour implémenter une table de hachage. Dans les applications pratiques, il est souvent nécessaire d’effectuer des opérations de recherche sur des chaînes. Par exemple, recherchez le nombre d'occurrences d'un certain mot-clé dans un texte ou recherchez toutes les lignes contenant une certaine chaîne. pour haut

Maîtrisez l'essence de Java Map, une compétence nécessaire pour les apprenants avancés Maîtrisez l'essence de Java Map, une compétence nécessaire pour les apprenants avancés Feb 19, 2024 pm 06:00 PM

JavaMap est une structure de données qui vous permet de stocker et de récupérer des valeurs à l'aide de clés. Les clés d'une Map sont uniques, ce qui signifie que vous ne pouvez pas stocker deux valeurs avec la même clé. Les valeurs de la carte peuvent être n'importe quel objet, y compris d'autres cartes. La carte a de nombreuses utilisations en Java. Par exemple, vous pouvez utiliser une carte pour stocker des ID utilisateur et des mots de passe, des ID de produits et des prix, ou des noms de fichiers et leur contenu. Les cartes sont également idéales pour stocker les paramètres de configuration dans une application. Il existe trois implémentations de Map intégrées en Java : HashMap, TreeMap et LinkedHashMap. HashMap est une implémentation de Map basée sur des tables de hachage, et c'est l'implémentation de Map la plus largement utilisée. TreeMap est basé sur

Comment implémenter une table de hachage en Python Comment implémenter une table de hachage en Python Jun 10, 2023 am 10:49 AM

La table de hachage est une structure de données importante largement utilisée en informatique. Il peut rapidement rechercher, insérer ou supprimer un élément spécifique dans de grandes quantités de données. Utiliser Python pour implémenter une table de hachage peut non seulement vous fournir une compréhension approfondie du mécanisme de fonctionnement interne d'une table de hachage, mais également améliorer vos capacités de programmation. Dans cet article, nous détaillerons comment implémenter une table de hachage en Python. Qu'est-ce qu'une table de hachage ? Une table de hachage est également appelée table de hachage. Il s'agit d'une méthode de stockage de valeurs-clés. Cela fonctionne en mappant la clé à la valeur

Tables de hachage et arbres rouge-noir dans le cadre de collection Java Tables de hachage et arbres rouge-noir dans le cadre de collection Java Apr 12, 2024 pm 02:42 PM

Les tables de hachage et les arbres rouge-noir sont les deux principales structures de données du cadre de collecte Java : les tables de hachage utilisent des fonctions de hachage pour insérer et rechercher rapidement, mais peuvent produire des conflits de hachage. L'arbre rouge-noir est un arbre de recherche binaire équilibré qui fournit des opérations de complexité logarithmique équilibrées et peut trier automatiquement.

See all articles