Arcane  4.2.1.0
Documentation utilisateur
Chargement...
Recherche...
Aucune correspondance
HashTableMap.h
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* HashTableMap.h (C) 2000-2026 */
9/* */
10/* Tableau associatif utilisant une table de hachage. */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCANE_UTILS_HASHTABLEMAP_H
13#define ARCANE_UTILS_HASHTABLEMAP_H
14/*---------------------------------------------------------------------------*/
15/*---------------------------------------------------------------------------*/
16
17#include "arcane/utils/HashTable.h"
18
19#include <iostream>
20
21/*---------------------------------------------------------------------------*/
22/*---------------------------------------------------------------------------*/
23
24namespace Arcane
25{
26
27/*---------------------------------------------------------------------------*/
28/*---------------------------------------------------------------------------*/
29
30template <typename KeyType, typename ValueType>
32
33/*---------------------------------------------------------------------------*/
34/*---------------------------------------------------------------------------*/
35/*!
36 * \internal
37 * \brief Table de hachage pour tableaux associatifs.
38
39 Cette table permet de stocker une valeur en fonction d'une clé. La
40 clé est de type \a KeyType et la valeur \a ValueType.
41
42 Cette table permet pour l'instant uniquement d'ajouter des valeurs.
43 La mémoire associée à chaque entrée du tableau est gérée par
44 un MultiBufferT.
45
46 Il est possible de spécifier une fonction de hachage différente de
47 la fonction par défaut en spécifiant le troisième paramètre
48 template \a KeyTraitsType.
49
50 Pour des raisons de performance, il est préférable que la taille
51 de la table (buckets) soit un nombre premier.
52 */
53template <typename KeyType, typename ValueType, typename KeyTraitsType = HashTraitsT<KeyType>>
55: public HashTableBase
56{
57 public:
58
59 typedef typename KeyTraitsType::KeyTypeConstRef KeyTypeConstRef;
60 typedef typename KeyTraitsType::KeyTypeValue KeyTypeValue;
61 typedef typename KeyTraitsType::Printable Printable;
62 typedef typename KeyTraitsType::HashValueType HashValueType;
65
66 public:
67
68 struct Data
69 {
70 public:
71
72 Data()
73 : m_key(KeyTypeValue())
75 {}
76
77 public:
78
79 Data* next() { return m_next; }
80 void setNext(Data* anext) { this->m_next = anext; }
81 KeyTypeConstRef key() { return m_key; }
82 const ValueType& value() const { return m_value; }
83 ValueType& value() { return m_value; }
84 //! Modifie la valeur de l'instance.
85 void setValue(const ValueType& avalue) { m_value = avalue; }
86 /*!
87 * \brief Change la valeur de la clé.
88 *
89 * Après avoir changé la valeur d'une ou plusieurs clés, il faut faire un rehash().
90 */
91 void setKey(const KeyType& new_key)
92 {
93 m_key = new_key;
94 }
95
96 public:
97
98 KeyTypeValue m_key; //!< Clé de recherche
99 ValueType m_value; //!< Valeur de l'élément
100 Data* m_next = nullptr; //! Elément suivant dans la table de hachage
101 };
102
103 public:
104
105 /*! \brief Crée une table de taille \a table_size
106 *
107 Si \a use_prime est vrai, utilise la fonction nearestPrimeNumber()
108 pour avoir une taille de taille qui est un nombre premier.
109 */
110 HashTableMapT(Integer table_size, bool use_prime)
111 : HashTableBase(table_size, use_prime)
112 , m_first_free(0)
113 , m_nb_collision(0)
114 , m_nb_direct(0)
115 , m_max_count(0)
116 {
117 m_buffer = new MultiBufferT<Data>(m_nb_bucket);
118 m_buckets.resize(m_nb_bucket);
119 m_buckets.fill(0);
120 _computeMaxCount();
121 }
122
123 /*! \brief Crée une table de taille \a table_size
124 *
125 Si \a use_prime est vrai, utilise la fonction nearestPrimeNumber()
126 pour avoir une taille de taille qui est un nombre premier.
127 */
128 HashTableMapT(Integer table_size, bool use_prime, Integer buffer_size)
129 : HashTableBase(table_size, use_prime)
130 , m_first_free(0)
131 , m_nb_collision(0)
132 , m_nb_direct(0)
133 {
134 m_buffer = new MultiBufferT<Data>(buffer_size);
135 m_buckets.resize(m_nb_bucket);
136 m_buckets.fill(0);
137 _computeMaxCount();
138 }
139
141 {
142 delete m_buffer;
143 }
144
145 //! Opérateur de recopie
146 ThatClass& operator=(const ThatClass& from)
147 {
148 if (&from == this)
149 return *this;
150 //cout << "** OPERATOR= this=" << this << '\n';
151 Integer nb_bucket = from.m_nb_bucket;
152 m_first_free = 0;
153 // Remet à zéro le compteur.
154 m_count = 0;
155 m_buckets.resize(nb_bucket);
156 m_buckets.fill(0);
157 _computeMaxCount();
158 delete m_buffer;
159 m_buffer = new MultiBufferT<Data>(nb_bucket);
160 ConstArrayView<Data*> from_buckets(from.buckets());
161 for (Integer i = 0; i < nb_bucket; ++i)
162 for (Data* data = from_buckets[i]; data; data = data->next())
163 _add(i, data->key(), data->value());
164 this->m_nb_bucket = nb_bucket;
165 return *this;
166 }
167
168 public:
169
170 //! \a true si une valeur avec la clé \a id est présente
171 bool hasKey(KeyTypeConstRef id)
172 {
173 Integer hf = _keyToBucket(id);
174 for (Data* i = m_buckets[hf]; i; i = i->m_next) {
175 if (i->key() == id)
176 return true;
177 }
178 return false;
179 }
180
181 //! Supprime tous les éléments de la table
182 void clear()
183 {
184 m_buckets.fill(0);
185 m_count = 0;
186 }
187
188 /*!
189 * \brief Recherche la valeur correspondant à la clé \a id.
190 *
191 * \return la structure associé à la clé \a id (0 si aucune)
192 */
193 Data* lookup(KeyTypeConstRef id)
194 {
195 return _lookup(id);
196 }
197
198 /*!
199 * \brief Recherche la valeur correspondant à la clé \a id.
200 *
201 * \return la structure associé à la clé \a id (0 si aucune)
202 */
203 const Data* lookup(KeyTypeConstRef id) const
204 {
205 return _lookup(id);
206 }
207
208 /*!
209 * \brief Recherche la valeur correspondant à la clé \a id.
210 *
211 * Une exception est générée si la valeur n'est pas trouvé.
212 */
213 ValueType& lookupValue(KeyTypeConstRef id)
214 {
215 Data* ht = _lookup(id);
216 if (!ht) {
217 this->_throwNotFound(id, Printable());
218 }
219 return ht->value();
220 }
221
222 /*!
223 * \brief Recherche la valeur correspondant à la clé \a id.
224 *
225 * Une exception est générée si la valeur n'est pas trouvé.
226 */
227 ValueType& operator[](KeyTypeConstRef id)
228 {
229 return lookupValue(id);
230 }
231
232 /*!
233 * \brief Recherche la valeur correspondant à la clé \a id.
234 *
235 * Une exception est générée si la valeur n'est pas trouvé.
236 */
237 const ValueType& lookupValue(KeyTypeConstRef id) const
238 {
239 const Data* ht = _lookup(id);
240 if (!ht) {
241 this->_throwNotFound(id, Printable());
242 }
243 return ht->m_value;
244 }
245
246 /*!
247 * \brief Recherche la valeur correspondant à la clé \a id.
248 *
249 * Une exception est générée si la valeur n'est pas trouvé.
250 */
251 const ValueType& operator[](KeyTypeConstRef id) const
252 {
253 return lookupValue(id);
254 }
255
256 /*!
257 * \brief Ajoute la valeur \a value correspondant à la clé \a id
258 *
259 * Si une valeur correspondant à \a id existe déjà, elle est remplacée.
260 *
261 * \retval true si la clé est ajoutée
262 * \retval false si la clé existe déjà et est remplacée
263 */
264 bool add(KeyTypeConstRef id, const ValueType& value)
265 {
266 Integer hf = _keyToBucket(id);
267 Data* ht = _lookupBucket(hf, id);
268 if (ht) {
269 ht->m_value = value;
270 return false;
271 }
272 _add(hf, id, value);
273 _checkResize();
274 return true;
275 }
276
277 /*!
278 * \brief Supprime la valeur associée à la clé \a id
279 */
280 void remove(KeyTypeConstRef id)
281 {
282 Integer hf = _keyToBucket(id);
283 Data* ht = _removeBucket(hf, id);
284 ht->setNext(m_first_free);
285 m_first_free = ht;
286 }
287
288 /*!
289 * \brief Recherche ou ajoute la valeur correspondant à la clé \a id.
290 *
291 * Si la clé \a id est déjà dans la table, retourne une référence sur cette
292 * valeur et positionne \a is_add à \c false. Sinon, ajoute la clé \a id
293 * avec pour valeur \a value et positionne \a is_add à \c true.
294 *
295 * La structure retournée n'est jamais nul et peut être conservée car elle
296 * ne change pas d'adresse tant que cette instance de la table de hachage existe
297 */
298 Data* lookupAdd(KeyTypeConstRef id, const ValueType& value, bool& is_add)
299 {
300 HashValueType hf = _applyHash(id);
301 Data* ht = _lookupBucket(_hashValueToBucket(hf), id);
302 if (ht) {
303 is_add = false;
304 return ht;
305 }
306 is_add = true;
307 // Toujours faire le resize avant de retourner le add
308 // car cela peut invalider le Data*
309 _checkResize();
310 ht = _add(_hashValueToBucket(hf), id, value);
311 return ht;
312 }
313
314 /*!
315 * \brief Recherche ou ajoute la valeur correspondant à la clé \a id.
316 *
317 * Si la clé \a id est déjà dans la table, retourne une référence sur cette
318 * valeur et positionne \a is_add à \c false. Sinon, ajoute la clé \a id
319 * avec pour valeur \a ValueType() (qui doit exister).
320 *
321 * La structure retournée n'est jamais nul et peut être conservée car elle
322 * ne change pas d'adresse tant que cette instance de la table de hachage existe
323 */
324 Data* lookupAdd(KeyTypeConstRef id)
325 {
326 HashValueType hf = _applyHash(id);
327 Data* ht = _lookupBucket(_hashValueToBucket(hf), id);
328 if (!ht) {
329 // Toujours faire le resize avant de retourner le add
330 // car cela peut invalider le Data*
331 _checkResize();
332 // Le resize provoque change le bucket associé à une clé
333 ht = _add(_hashValueToBucket(hf), id, ValueType());
334 }
335 return ht;
336 }
337
338 /*!
339 * \brief Ajoute la valeur \a value correspondant à la clé \a id
340 *
341 * Si une valeur correspondant à \a id existe déjà, le résultat est
342 * indéfini.
343 */
344 void nocheckAdd(KeyTypeConstRef id, const ValueType& value)
345 {
346 _checkResize();
347 Integer hf = _keyToBucket(id);
348 _add(hf, id, value);
349 }
350
351 ArrayView<Data*> buckets()
352 {
353 return m_buckets;
354 }
355
356 ConstArrayView<Data*> buckets() const
357 {
358 return m_buckets;
359 }
360
361 //! Redimensionne la table de hachage
362 void resize(Integer new_size, bool use_prime = false)
363 {
364 if (use_prime)
365 new_size = this->nearestPrimeNumber(new_size);
366 if (new_size == 0) {
367 m_nb_bucket = new_size;
368 clear();
369 return;
370 }
371 if (new_size == m_nb_bucket)
372 return;
373 _rehash(new_size);
374 }
375
376 //! Repositionne les données après changement de valeur des clés
377 void rehash()
378 {
379 _rehash(m_nb_bucket);
380 }
381
382 public:
383
384 //! Applique le fonctor \a f à tous les éléments de la collection
385 template <class Lambda> void
386 each(const Lambda& lambda)
387 {
388 for (Integer k = 0, n = m_buckets.size(); k < n; ++k) {
389 Data* nbid = m_buckets[k];
390 for (; nbid; nbid = nbid->next()) {
391 lambda(nbid);
392 }
393 }
394 }
395
396 /*!
397 * \brief Applique le fonctor \a f à tous les éléments de la collection
398 * et utilise x->value() (de type ValueType) comme argument.
399 */
400 template <class Lambda> void
401 eachValue(const Lambda& lambda)
402 {
403 for (Integer k = 0, n = m_buckets.size(); k < n; ++k) {
404 Data* nbid = m_buckets[k];
405 for (; nbid; nbid = nbid->next()) {
406 lambda(nbid->value());
407 }
408 }
409 }
410
411 private:
412
413 //! Repositionne les données après changement de valeur des clés
414 void _rehash(Integer new_size)
415 {
416 //todo: supprimer l'allocation de ce tableau
417 UniqueArray<Data*> old_buckets(m_buckets);
418 m_count = 0;
419 m_nb_bucket = new_size;
420 m_buckets.resize(new_size);
421 m_buckets.fill(0);
422 MultiBufferT<Data>* old_buffer = m_buffer;
423 m_first_free = 0;
424 m_buffer = new MultiBufferT<Data>(m_nb_bucket);
425 for (Integer z = 0, zs = old_buckets.size(); z < zs; ++z) {
426 for (Data* i = old_buckets[z]; i; i = i->next()) {
427 Data* current = i;
428 {
429 _add(_keyToBucket(current->key()), current->key(), current->value());
430 //Data* new_data = m_buffer->allocOne();
431 //new_data->setValue(current->value());
432 //_baseAdd(_hash(current->key()),current->key(),new_data);
433 }
434 }
435 }
436 delete old_buffer;
437 _computeMaxCount();
438 }
439
440 private:
441
442 MultiBufferT<Data>* m_buffer = nullptr; //!< Tampon d'allocation des valeurs
443 Data* m_first_free = nullptr; //!< Pointeur vers le premier Data utilisable
444
445 public:
446
447 mutable Int64 m_nb_collision = 0;
448 mutable Int64 m_nb_direct = 0;
449
450 private:
451
452 Data* _add(Integer bucket, KeyTypeConstRef key, const ValueType& value)
453 {
454 Data* hd = 0;
455 if (m_first_free) {
456 hd = m_first_free;
457 m_first_free = m_first_free->next();
458 }
459 else
460 hd = m_buffer->allocOne();
461 hd->setValue(value);
462 _baseAdd(bucket, key, hd);
463 return hd;
464 }
465
466 HashValueType _applyHash(KeyTypeConstRef id) const
467 {
468 //return (Integer)(KeyTraitsType::hashFunction(id) % m_nb_bucket);
469 return KeyTraitsType::hashFunction(id);
470 }
471
472 Integer _keyToBucket(KeyTypeConstRef id) const
473 {
474 return (Integer)(_applyHash(id) % m_nb_bucket);
475 }
476
477 Integer _hashValueToBucket(KeyTypeValue id) const
478 {
479 return (Integer)(id % m_nb_bucket);
480 }
481
482 Data* _baseLookupBucket(Integer bucket, KeyTypeConstRef id) const
483 {
484 for (Data* i = m_buckets[bucket]; i; i = i->next()) {
485 if (!(i->key() == id)) {
486 ++m_nb_collision;
487 continue;
488 }
489 ++m_nb_direct;
490 return i;
491 }
492 return 0;
493 }
494
495 Data* _baseRemoveBucket(Integer bucket, KeyTypeConstRef id)
496 {
497 Data* i = m_buckets[bucket];
498 if (i) {
499 if (i->m_key == id) {
500 m_buckets[bucket] = i->next();
501 --m_count;
502 return i;
503 }
504 for (; i->next(); i = i->next()) {
505 if (i->next()->key() == id) {
506 Data* r = i->next();
507 i->setNext(i->next()->next());
508 --m_count;
509 return r;
510 }
511 }
512 }
513 this->_throwNotFound(id, Printable());
514 return 0;
515 }
516
517 inline Data* _baseLookup(KeyTypeConstRef id) const
518 {
519 return _baseLookupBucket(_keyToBucket(id), id);
520 }
521
522 inline Data* _baseRemove(KeyTypeConstRef id)
523 {
524 return _baseRemoveBucket(_keyToBucket(id), id);
525 }
526
527 void _baseAdd(Integer bucket, KeyTypeConstRef id, Data* hd)
528 {
529 Data* buck = m_buckets[bucket];
530 hd->m_key = id;
531 hd->m_next = buck;
532 m_buckets[bucket] = hd;
533 ++m_count;
534 }
535
536 Data* _lookup(KeyTypeConstRef id)
537 {
538 return _baseLookup(id);
539 }
540
541 const Data* _lookup(KeyTypeConstRef id) const
542 {
543 return _baseLookup(id);
544 }
545
546 Data* _lookupBucket(Integer bucket, KeyTypeConstRef id) const
547 {
548 return _baseLookupBucket(bucket, id);
549 }
550
551 Data* _removeBucket(Integer bucket, KeyTypeConstRef id)
552 {
553 return _baseRemoveBucket(bucket, id);
554 }
555
556 void _checkResize()
557 {
558 // Retaille si besoin.
559 if (m_count > m_max_count) {
560 //cout << "** AVANT REDIMENSIONNEMENT DU COMPARTIMENT this=" << this << " count=" << m_count
561 // << " bucket=" << m_nb_bucket << " m_max_count=" << m_max_count
562 // << " memory=" << (m_buckets.capacity()*sizeof(Data*)) << '\n';
563 //_print(Printable());
564 // Pour les grosses tables, augmente moins vite pour limiter la
565 // consommation memoire
566 if (m_nb_bucket > 200000) {
567 resize((Integer)(1.3 * m_nb_bucket), true);
568 }
569 else if (m_nb_bucket > 10000) {
570 resize((Integer)(1.5 * m_nb_bucket), true);
571 }
572 else
573 resize(2 * m_nb_bucket, true);
574 //cout << "** APRÈS REDIMENSIONNEMENT DU COMPARTIMENT this=" << this << " count=" << m_count
575 // << " bucket=" << m_nb_bucket << " m_max_count=" << m_max_count
576 // << " memory=" << (m_buckets.capacity()*sizeof(Data*)) << '\n';
577 //_print(Printable());
578 //std::cout.flush();
579 }
580 }
581
582 void _print(FalseType)
583 {
584 }
585
586 void _print(TrueType)
587 {
588 for (Integer z = 0, zs = m_buckets.size(); z < zs; ++z) {
589 for (Data* i = m_buckets[z]; i; i = i->next()) {
590 std::cout << "* KEY=" << i->key() << " bucket=" << z << '\n';
591 }
592 }
593 }
594
595 void _throwNotFound ARCANE_NORETURN(KeyTypeConstRef, FalseType) const
596 {
597 HashTableBase::_throwNotFound();
598 }
599
600 void _throwNotFound ARCANE_NORETURN(KeyTypeConstRef id, TrueType) const
601 {
602 std::cout << "ERROR: can not find key=" << id << " bucket=" << _keyToBucket(id) << "\n";
603 std::cout.flush();
604 HashTableBase::_throwNotFound();
605 }
606
607 void _computeMaxCount()
608 {
609 m_max_count = (Integer)(m_nb_bucket * 0.85);
610 }
611
612 private:
613
614 //! Nombre maximal d'élément avant retaillage
615 Integer m_max_count = 0;
616 UniqueArray<Data*> m_buckets; //! Tableau des buckets
617};
618
619/*---------------------------------------------------------------------------*/
620/*---------------------------------------------------------------------------*/
621
622/*!
623 * \internal
624 * \brief Énumérateur pour un HashTableMap.
625 */
626template <typename KeyType, typename ValueType>
627class HashTableMapEnumeratorT
628{
629 typedef HashTableMapT<KeyType, ValueType> HashType;
630 typedef typename HashType::Data Data;
631
632 public:
633
634 HashTableMapEnumeratorT(const HashType& rhs)
635 : m_buckets(rhs.buckets())
636 , m_current_data(0)
637 , m_current_bucket(-1)
638 {}
639
640 public:
641
642 bool operator++()
643 {
644 if (m_current_data)
645 m_current_data = m_current_data->next();
646 if (!m_current_data) {
647 while (m_current_data == 0 && (m_current_bucket + 1) < m_buckets.size()) {
648 ++m_current_bucket;
649 m_current_data = m_buckets[m_current_bucket];
650 }
651 }
652 return m_current_data != 0;
653 }
654 ValueType& operator*() { return m_current_data->value(); }
655 const ValueType& operator*() const { return m_current_data->value(); }
656 Data* data() { return m_current_data; }
657 const Data* data() const { return m_current_data; }
658
659 public:
660
661 ConstArrayView<Data*> m_buckets;
662 Data* m_current_data;
663 Integer m_current_bucket;
664};
665
666/*---------------------------------------------------------------------------*/
667/*---------------------------------------------------------------------------*/
668
669} // namespace Arcane
670
671/*---------------------------------------------------------------------------*/
672/*---------------------------------------------------------------------------*/
673
674#endif
Vue modifiable d'un tableau d'un type T.
Vue constante d'un tableau de type T.
HashTableBase(Integer table_size, bool use_prime)
Crée une table de taille table_size.
Definition HashTable.h:45
Integer m_nb_bucket
Nombre de buckets.
Definition HashTable.h:75
Integer nearestPrimeNumber(Integer n)
Retourne le nombre premier le plus proche de n par excès. Le nombre premier le plus proche et supérie...
Definition HashTable.cc:57
Integer m_count
Nombre d'éléments.
Definition HashTable.h:74
const ValueType & operator[](KeyTypeConstRef id) const
Recherche la valeur correspondant à la clé id.
void rehash()
Repositionne les données après changement de valeur des clés.
HashTableMapT(Integer table_size, bool use_prime, Integer buffer_size)
Crée une table de taille table_size.
void nocheckAdd(KeyTypeConstRef id, const ValueType &value)
Ajoute la valeur value correspondant à la clé id.
const Data * lookup(KeyTypeConstRef id) const
Recherche la valeur correspondant à la clé id.
Data * lookup(KeyTypeConstRef id)
Recherche la valeur correspondant à la clé id.
Data * lookupAdd(KeyTypeConstRef id, const ValueType &value, bool &is_add)
Recherche ou ajoute la valeur correspondant à la clé id.
void remove(KeyTypeConstRef id)
Supprime la valeur associée à la clé id.
void resize(Integer new_size, bool use_prime=false)
Redimensionne la table de hachage.
ValueType & lookupValue(KeyTypeConstRef id)
Recherche la valeur correspondant à la clé id.
ThatClass & operator=(const ThatClass &from)
Opérateur de recopie.
ValueType & operator[](KeyTypeConstRef id)
Recherche la valeur correspondant à la clé id.
bool add(KeyTypeConstRef id, const ValueType &value)
Ajoute la valeur value correspondant à la clé id.
void eachValue(const Lambda &lambda)
Applique le fonctor f à tous les éléments de la collection et utilise x->value() (de type ValueType) ...
void clear()
Supprime tous les éléments de la table.
void each(const Lambda &lambda)
Applique le fonctor f à tous les éléments de la collection.
const ValueType & lookupValue(KeyTypeConstRef id) const
Recherche la valeur correspondant à la clé id.
HashTableMapT(Integer table_size, bool use_prime)
Crée une table de taille table_size.
Data * lookupAdd(KeyTypeConstRef id)
Recherche ou ajoute la valeur correspondant à la clé id.
bool hasKey(KeyTypeConstRef id)
true si une valeur avec la clé id est présente
Vecteur 1D de données avec sémantique par valeur (style STL).
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
std::int64_t Int64
Type entier signé sur 64 bits.
Int32 Integer
Type représentant un entier.
void setValue(const ValueType &avalue)
Modifie la valeur de l'instance.
ValueType m_value
Valeur de l'élément.
KeyTypeValue m_key
Clé de recherche.
void setKey(const KeyType &new_key)
Change la valeur de la clé.