1 | /** |
---|
2 | @file Table.h |
---|
3 | |
---|
4 | Templated hash table class. |
---|
5 | |
---|
6 | @maintainer Morgan McGuire, matrix@graphics3d.com |
---|
7 | @created 2001-04-22 |
---|
8 | @edited 2006-10-14 |
---|
9 | Copyright 2000-2006, Morgan McGuire. |
---|
10 | All rights reserved. |
---|
11 | */ |
---|
12 | |
---|
13 | #ifndef G3D_TABLE_H |
---|
14 | #define G3D_TABLE_H |
---|
15 | |
---|
16 | #include "G3D/platform.h" |
---|
17 | #include "G3D/Array.h" |
---|
18 | #include "G3D/debug.h" |
---|
19 | #include "G3D/System.h" |
---|
20 | #include "G3D/g3dmath.h" |
---|
21 | #include "G3D/Crypto.h" |
---|
22 | #include <cstddef> |
---|
23 | #include <string> |
---|
24 | |
---|
25 | #ifdef G3D_WIN32 |
---|
26 | # pragma warning (push) |
---|
27 | // Debug name too long warning |
---|
28 | # pragma warning (disable : 4786) |
---|
29 | #endif |
---|
30 | |
---|
31 | |
---|
32 | template<typename Key> |
---|
33 | struct GHashCode{}; |
---|
34 | |
---|
35 | template <> |
---|
36 | struct GHashCode<int> |
---|
37 | { |
---|
38 | size_t operator()(int key) const { return static_cast<size_t>(key); } |
---|
39 | }; |
---|
40 | |
---|
41 | template <> |
---|
42 | struct GHashCode<G3D::uint32> |
---|
43 | { |
---|
44 | size_t operator()(G3D::uint32 key) const { return static_cast<size_t>(key); } |
---|
45 | }; |
---|
46 | |
---|
47 | template <> |
---|
48 | struct GHashCode<G3D::uint64> |
---|
49 | { |
---|
50 | size_t operator()(G3D::uint64 key) const { return static_cast<size_t>(key); } |
---|
51 | }; |
---|
52 | |
---|
53 | template <> |
---|
54 | struct GHashCode<void*> |
---|
55 | { |
---|
56 | size_t operator()(const void* key) const { return reinterpret_cast<size_t>(key); } |
---|
57 | }; |
---|
58 | |
---|
59 | template<class T> |
---|
60 | struct GHashCode<T*> |
---|
61 | { |
---|
62 | size_t operator()(const T* key) const { return reinterpret_cast<size_t>(key); } |
---|
63 | }; |
---|
64 | |
---|
65 | template <> |
---|
66 | struct GHashCode<const std::string> |
---|
67 | { |
---|
68 | size_t operator()(const std::string& key) const { return static_cast<size_t>(G3D::Crypto::crc32(key.c_str(), key.size())); } |
---|
69 | }; |
---|
70 | |
---|
71 | template <> |
---|
72 | struct GHashCode<std::string> |
---|
73 | { |
---|
74 | size_t operator()(const std::string& key) const { return static_cast<size_t>(G3D::Crypto::crc32(key.c_str(), key.size())); } |
---|
75 | }; |
---|
76 | |
---|
77 | namespace G3D { |
---|
78 | |
---|
79 | |
---|
80 | /** |
---|
81 | An unordered data structure mapping keys to values. |
---|
82 | |
---|
83 | Key must be a pointer, an int, a std::string, a class with a hashCode() method, |
---|
84 | or provide overloads for: |
---|
85 | |
---|
86 | <PRE> |
---|
87 | template<> struct GHashCode<class Key> { |
---|
88 | size_t operator()(Key key) const { return reinterpret_cast<size_t>( ... ); } |
---|
89 | }; |
---|
90 | |
---|
91 | bool operator==(const Key&, const Key&); |
---|
92 | </PRE> |
---|
93 | |
---|
94 | G3D pre-defines GHashCode functions for common types (like <CODE>int</CODE> and <CODE>std::string</CODE>). |
---|
95 | If you use a Table with a different type you must write those functions yourself. For example, |
---|
96 | an enum would use: |
---|
97 | |
---|
98 | <PRE> |
---|
99 | template<> struct GHashCode<MyEnum> { |
---|
100 | size_t operator()(MyEnum key) const { return reinterpret_cast<size_t>( key ); } |
---|
101 | }; |
---|
102 | </PRE> |
---|
103 | |
---|
104 | And rely on the default enum operator==. |
---|
105 | |
---|
106 | |
---|
107 | Periodically check that debugGetLoad() is low (> 0.1). When it gets near |
---|
108 | 1.0 your hash function is badly designed and maps too many inputs to |
---|
109 | the same output. |
---|
110 | */ |
---|
111 | template<class Key, class Value, class HashFunc = GHashCode<Key> > |
---|
112 | class Table { |
---|
113 | public: |
---|
114 | |
---|
115 | /** |
---|
116 | The pairs returned by iterator. |
---|
117 | */ |
---|
118 | class Entry { |
---|
119 | public: |
---|
120 | Key key; |
---|
121 | Value value; |
---|
122 | }; |
---|
123 | |
---|
124 | private: |
---|
125 | /** |
---|
126 | Linked list nodes used internally by HashTable. |
---|
127 | */ |
---|
128 | class Node { |
---|
129 | public: |
---|
130 | size_t hashCode; |
---|
131 | Entry entry; |
---|
132 | Node* next; |
---|
133 | |
---|
134 | |
---|
135 | /** Provide pooled allocation for speed. */ |
---|
136 | inline void* operator new (size_t size) { |
---|
137 | return System::malloc(size); |
---|
138 | } |
---|
139 | |
---|
140 | inline void operator delete (void* p) { |
---|
141 | System::free(p); |
---|
142 | } |
---|
143 | |
---|
144 | |
---|
145 | Node(Key key, Value value, size_t hashCode, Node* next) { |
---|
146 | this->entry.key = key; |
---|
147 | this->entry.value = value; |
---|
148 | this->hashCode = hashCode; |
---|
149 | this->next = next; |
---|
150 | } |
---|
151 | |
---|
152 | /** |
---|
153 | Clones a whole chain; |
---|
154 | */ |
---|
155 | Node* clone() { |
---|
156 | return new Node(this->entry.key, this->entry.value, hashCode, (next == NULL) ? NULL : next->clone()); |
---|
157 | } |
---|
158 | }; |
---|
159 | |
---|
160 | HashFunc m_HashFunc; |
---|
161 | |
---|
162 | /** |
---|
163 | Number of elements in the table. |
---|
164 | */ |
---|
165 | size_t _size; |
---|
166 | |
---|
167 | /** |
---|
168 | Array of Node*. |
---|
169 | We don't use Array<Node*> because Table is lower level. |
---|
170 | Some elements may be NULL. |
---|
171 | */ |
---|
172 | Node** bucket; |
---|
173 | |
---|
174 | /** |
---|
175 | Length of the bucket array. |
---|
176 | */ |
---|
177 | size_t numBuckets; |
---|
178 | |
---|
179 | /** |
---|
180 | Re-hashes for a larger bucket size. |
---|
181 | */ |
---|
182 | void resize(size_t numBuckets) { |
---|
183 | |
---|
184 | Node** oldBucket = bucket; |
---|
185 | bucket = (Node**)System::alignedMalloc(sizeof(Node*) * numBuckets, 16); |
---|
186 | System::memset(bucket, 0, sizeof(Node*) * numBuckets); |
---|
187 | |
---|
188 | for (size_t b = 0; b < this->numBuckets; b++) { |
---|
189 | Node* node = oldBucket[b]; |
---|
190 | |
---|
191 | while (node != NULL) { |
---|
192 | Node* nextNode = node->next; |
---|
193 | |
---|
194 | // insert at the head of the list for bucket[i] |
---|
195 | size_t i = node->hashCode % numBuckets; |
---|
196 | node->next = bucket[i]; |
---|
197 | bucket[i] = node; |
---|
198 | |
---|
199 | node = nextNode; |
---|
200 | } |
---|
201 | } |
---|
202 | |
---|
203 | System::alignedFree(oldBucket); |
---|
204 | this->numBuckets = numBuckets; |
---|
205 | } |
---|
206 | |
---|
207 | void copyFrom(const Table<Key, Value>& h) { |
---|
208 | this->_size = h._size; |
---|
209 | this->numBuckets = h.numBuckets; |
---|
210 | this->bucket = (Node**)System::alignedMalloc(sizeof(Node*) * numBuckets, 16); |
---|
211 | System::memset(this->bucket, 0, sizeof(Node*) * numBuckets); |
---|
212 | for (size_t b = 0; b < this->numBuckets; b++) { |
---|
213 | if (h.bucket[b] != NULL) { |
---|
214 | bucket[b] = h.bucket[b]->clone(); |
---|
215 | } |
---|
216 | } |
---|
217 | } |
---|
218 | |
---|
219 | /** |
---|
220 | Frees the heap structures for the nodes. |
---|
221 | */ |
---|
222 | void freeMemory() { |
---|
223 | for (size_t b = 0; b < numBuckets; b++) { |
---|
224 | Node* node = bucket[b]; |
---|
225 | while (node != NULL) { |
---|
226 | Node* next = node->next; |
---|
227 | delete node; |
---|
228 | node = next; |
---|
229 | } |
---|
230 | } |
---|
231 | System::alignedFree(bucket); |
---|
232 | bucket = NULL; |
---|
233 | numBuckets = 0; |
---|
234 | _size = 0; |
---|
235 | } |
---|
236 | |
---|
237 | public: |
---|
238 | |
---|
239 | /** |
---|
240 | Creates an empty hash table. This causes some heap allocation to occur. |
---|
241 | */ |
---|
242 | Table() { |
---|
243 | numBuckets = 10; |
---|
244 | _size = 0; |
---|
245 | bucket = (Node**)System::alignedMalloc(sizeof(Node*) * numBuckets, 16); |
---|
246 | System::memset(bucket, 0, sizeof(Node*) * numBuckets); |
---|
247 | } |
---|
248 | |
---|
249 | /** |
---|
250 | Destroys all of the memory allocated by the table, but does <B>not</B> |
---|
251 | call delete on keys or values if they are pointers. If you want to |
---|
252 | deallocate things that the table points at, use getKeys() and Array::deleteAll() |
---|
253 | to delete them. |
---|
254 | */ |
---|
255 | virtual ~Table() { |
---|
256 | freeMemory(); |
---|
257 | } |
---|
258 | |
---|
259 | Table(const Table<Key, Value>& h) { |
---|
260 | this->copyFrom(h); |
---|
261 | } |
---|
262 | |
---|
263 | Table& operator=(const Table<Key, Value>& h) { |
---|
264 | // No need to copy if the argument is this |
---|
265 | if (this != &h) { |
---|
266 | // Free the existing nodes |
---|
267 | freeMemory(); |
---|
268 | this->copyFrom(h); |
---|
269 | } |
---|
270 | return *this; |
---|
271 | } |
---|
272 | |
---|
273 | /** |
---|
274 | Returns the length of the deepest bucket. |
---|
275 | */ |
---|
276 | size_t debugGetDeepestBucketSize() const { |
---|
277 | size_t deepest = 0; |
---|
278 | |
---|
279 | for (size_t b = 0; b < numBuckets; b++) { |
---|
280 | size_t count = 0; |
---|
281 | Node* node = bucket[b]; |
---|
282 | while (node != NULL) { |
---|
283 | node = node->next; |
---|
284 | ++count; |
---|
285 | } |
---|
286 | |
---|
287 | if (count > deepest) { |
---|
288 | deepest = count; |
---|
289 | } |
---|
290 | } |
---|
291 | |
---|
292 | return deepest; |
---|
293 | } |
---|
294 | |
---|
295 | /** |
---|
296 | A small load (close to zero) means the hash table is acting very |
---|
297 | efficiently most of the time. A large load (close to 1) means |
---|
298 | the hash table is acting poorly-- all operations will be very slow. |
---|
299 | A large load will result from a bad hash function that maps too |
---|
300 | many keys to the same code. |
---|
301 | */ |
---|
302 | double debugGetLoad() const { |
---|
303 | return debugGetDeepestBucketSize() / (double)size(); |
---|
304 | } |
---|
305 | |
---|
306 | /** |
---|
307 | Returns the number of buckets. |
---|
308 | */ |
---|
309 | size_t debugGetNumBuckets() const { |
---|
310 | return numBuckets; |
---|
311 | } |
---|
312 | |
---|
313 | /** |
---|
314 | C++ STL style iterator variable. See begin(). |
---|
315 | */ |
---|
316 | class Iterator { |
---|
317 | private: |
---|
318 | friend class Table<Key, Value>; |
---|
319 | |
---|
320 | /** |
---|
321 | Bucket index. |
---|
322 | */ |
---|
323 | size_t index; |
---|
324 | |
---|
325 | /** |
---|
326 | Linked list node. |
---|
327 | */ |
---|
328 | Node* node; |
---|
329 | Table<Key, Value>* table; |
---|
330 | size_t numBuckets; |
---|
331 | Node** bucket; |
---|
332 | bool isDone; |
---|
333 | |
---|
334 | /** |
---|
335 | Creates the end iterator. |
---|
336 | */ |
---|
337 | Iterator(const Table<Key, Value>* table) : table(const_cast<Table<Key, Value>*>(table)) { |
---|
338 | isDone = true; |
---|
339 | } |
---|
340 | |
---|
341 | Iterator(const Table<Key, Value>* table, size_t numBuckets, Node** bucket) : |
---|
342 | table(const_cast<Table<Key, Value>*>(table)), |
---|
343 | numBuckets(numBuckets), |
---|
344 | bucket(bucket) { |
---|
345 | |
---|
346 | if (numBuckets == 0) { |
---|
347 | // Empty table |
---|
348 | isDone = true; |
---|
349 | return; |
---|
350 | } |
---|
351 | |
---|
352 | index = 0; |
---|
353 | node = bucket[index]; |
---|
354 | isDone = false; |
---|
355 | findNext(); |
---|
356 | } |
---|
357 | |
---|
358 | /** |
---|
359 | Finds the next element, setting isDone if one can't be found. |
---|
360 | Looks at the current element first. |
---|
361 | */ |
---|
362 | void findNext() { |
---|
363 | while (node == NULL) { |
---|
364 | index++; |
---|
365 | if (index >= numBuckets) { |
---|
366 | isDone = true; |
---|
367 | break; |
---|
368 | } else { |
---|
369 | node = bucket[index]; |
---|
370 | } |
---|
371 | } |
---|
372 | } |
---|
373 | |
---|
374 | public: |
---|
375 | inline bool operator!=(const Iterator& other) const { |
---|
376 | return !(*this == other); |
---|
377 | } |
---|
378 | |
---|
379 | bool operator==(const Iterator& other) const { |
---|
380 | if (other.isDone || isDone) { |
---|
381 | // Common case; check against isDone. |
---|
382 | return (isDone == other.isDone) && (other.table == table); |
---|
383 | } else { |
---|
384 | return |
---|
385 | (table == other.table) && |
---|
386 | (node == other.node) && |
---|
387 | (index == other.index); |
---|
388 | } |
---|
389 | } |
---|
390 | |
---|
391 | /** |
---|
392 | Pre increment. |
---|
393 | */ |
---|
394 | Iterator& operator++() { |
---|
395 | node = node->next; |
---|
396 | findNext(); |
---|
397 | return *this; |
---|
398 | } |
---|
399 | |
---|
400 | /** |
---|
401 | Post increment (slower than preincrement). |
---|
402 | */ |
---|
403 | Iterator operator++(int) { |
---|
404 | Iterator old = *this; |
---|
405 | ++(*this); |
---|
406 | return old; |
---|
407 | } |
---|
408 | |
---|
409 | const Entry& operator*() const { |
---|
410 | return node->entry; |
---|
411 | } |
---|
412 | |
---|
413 | Entry* operator->() const { |
---|
414 | return &(node->entry); |
---|
415 | } |
---|
416 | |
---|
417 | operator Entry*() const { |
---|
418 | return &(node->entry); |
---|
419 | } |
---|
420 | }; |
---|
421 | |
---|
422 | |
---|
423 | /** |
---|
424 | C++ STL style iterator method. Returns the first Entry, which |
---|
425 | contains a key and value. Use preincrement (++entry) to get to |
---|
426 | the next element. Do not modify the table while iterating. |
---|
427 | */ |
---|
428 | Iterator begin() const { |
---|
429 | return Iterator(this, numBuckets, bucket); |
---|
430 | } |
---|
431 | |
---|
432 | /** |
---|
433 | C++ STL style iterator method. Returns one after the last iterator |
---|
434 | element. |
---|
435 | */ |
---|
436 | const Iterator end() const { |
---|
437 | return Iterator(this); |
---|
438 | } |
---|
439 | |
---|
440 | /** |
---|
441 | Removes all elements |
---|
442 | */ |
---|
443 | void clear() { |
---|
444 | freeMemory(); |
---|
445 | numBuckets = 20; |
---|
446 | _size = 0; |
---|
447 | bucket = (Node**)System::alignedMalloc(sizeof(Node*) * numBuckets, 16); |
---|
448 | System::memset(bucket, 0, sizeof(Node*) * numBuckets); |
---|
449 | } |
---|
450 | |
---|
451 | |
---|
452 | /** |
---|
453 | Returns the number of keys. |
---|
454 | */ |
---|
455 | size_t size() const { |
---|
456 | return _size; |
---|
457 | } |
---|
458 | |
---|
459 | |
---|
460 | /** |
---|
461 | If you insert a pointer into the key or value of a table, you are |
---|
462 | responsible for deallocating the object eventually. Inserting |
---|
463 | key into a table is O(1), but may cause a potentially slow rehashing. |
---|
464 | */ |
---|
465 | void set(const Key& key, const Value& value) { |
---|
466 | size_t code = m_HashFunc(key); |
---|
467 | size_t b = code % numBuckets; |
---|
468 | |
---|
469 | // Go to the bucket |
---|
470 | Node* n = bucket[b]; |
---|
471 | |
---|
472 | // No bucket, so this must be the first |
---|
473 | if (n == NULL) { |
---|
474 | bucket[b] = new Node(key, value, code, NULL); |
---|
475 | ++_size; |
---|
476 | return; |
---|
477 | } |
---|
478 | |
---|
479 | size_t bucketLength = 1; |
---|
480 | |
---|
481 | // Sometimes a bad hash code will cause all elements |
---|
482 | // to collide. Detect this case and don't rehash when |
---|
483 | // it occurs; nothing good will come from the rehashing. |
---|
484 | bool allSameCode = true; |
---|
485 | |
---|
486 | // Try to find the node |
---|
487 | do { |
---|
488 | allSameCode = allSameCode && (code == n->hashCode); |
---|
489 | |
---|
490 | if ((code == n->hashCode) && (n->entry.key == key)) { |
---|
491 | // Replace the existing node. |
---|
492 | n->entry.value = value; |
---|
493 | return; |
---|
494 | } |
---|
495 | |
---|
496 | n = n->next; |
---|
497 | ++bucketLength; |
---|
498 | } while (n != NULL); |
---|
499 | |
---|
500 | const size_t maxBucketLength = 5; |
---|
501 | if ((bucketLength > maxBucketLength) & ! allSameCode && (numBuckets < _size * 20)) { |
---|
502 | // This bucket was really large; rehash if all elements |
---|
503 | // don't have the same hashcode the number of buckets is reasonable. |
---|
504 | resize(numBuckets * 2 + 1); |
---|
505 | } |
---|
506 | |
---|
507 | // Not found; insert at the head. |
---|
508 | b = code % numBuckets; |
---|
509 | bucket[b] = new Node(key, value, code, bucket[b]); |
---|
510 | ++_size; |
---|
511 | } |
---|
512 | |
---|
513 | /** |
---|
514 | Removes an element from the table if it is present. It is an error |
---|
515 | to remove an element that isn't present. |
---|
516 | */ |
---|
517 | void remove(const Key& key) { |
---|
518 | |
---|
519 | size_t code = m_HashFunc(key); |
---|
520 | size_t b = code % numBuckets; |
---|
521 | |
---|
522 | // Go to the bucket |
---|
523 | Node* n = bucket[b]; |
---|
524 | |
---|
525 | // Make sure it was found |
---|
526 | alwaysAssertM(n != NULL, "Tried to remove a key that was not in the table."); |
---|
527 | |
---|
528 | Node* previous = NULL; |
---|
529 | |
---|
530 | // Try to find the node |
---|
531 | do { |
---|
532 | if ((code == n->hashCode) && (n->entry.key == key)) { |
---|
533 | // This is the node; remove it |
---|
534 | if (previous == NULL) { |
---|
535 | bucket[b] = n->next; |
---|
536 | } else { |
---|
537 | previous->next = n->next; |
---|
538 | } |
---|
539 | // Delete the node |
---|
540 | delete n; |
---|
541 | --_size; |
---|
542 | return; |
---|
543 | } |
---|
544 | |
---|
545 | previous = n; |
---|
546 | n = n->next; |
---|
547 | } while (n != NULL); |
---|
548 | |
---|
549 | |
---|
550 | alwaysAssertM(false, "Tried to remove a key that was not in the table."); |
---|
551 | } |
---|
552 | |
---|
553 | /** |
---|
554 | Returns the value associated with key. |
---|
555 | @deprecated Use get(key, val) or |
---|
556 | */ |
---|
557 | Value& get(const Key& key) const { |
---|
558 | |
---|
559 | size_t code = m_HashFunc(key); |
---|
560 | size_t b = code % numBuckets; |
---|
561 | |
---|
562 | Node* node = bucket[b]; |
---|
563 | |
---|
564 | while (node != NULL) { |
---|
565 | if ((node->hashCode == code) && (node->entry.key == key)) { |
---|
566 | return node->entry.value; |
---|
567 | } |
---|
568 | node = node->next; |
---|
569 | } |
---|
570 | |
---|
571 | debugAssertM(false, "Key not found"); |
---|
572 | // The next line is here just to make |
---|
573 | // a compiler warning go away. |
---|
574 | return node->entry.value; |
---|
575 | } |
---|
576 | |
---|
577 | /** |
---|
578 | If the key is present in the table, val is set to the associated value and returns true. |
---|
579 | If the key is not present, returns false. |
---|
580 | */ |
---|
581 | bool get(const Key& key, Value& val) const { |
---|
582 | size_t code = m_HashFunc(key); |
---|
583 | size_t b = code % numBuckets; |
---|
584 | |
---|
585 | Node* node = bucket[b]; |
---|
586 | |
---|
587 | while (node != NULL) { |
---|
588 | if ((node->hashCode == code) && (node->entry.key == key)) { |
---|
589 | // found key |
---|
590 | val = node->entry.value; |
---|
591 | return true; |
---|
592 | } |
---|
593 | node = node->next; |
---|
594 | } |
---|
595 | |
---|
596 | // Failed to find key |
---|
597 | return false; |
---|
598 | } |
---|
599 | |
---|
600 | /** |
---|
601 | Returns true if key is in the table. |
---|
602 | */ |
---|
603 | bool containsKey(const Key& key) const { |
---|
604 | size_t code = m_HashFunc(key); |
---|
605 | size_t b = code % numBuckets; |
---|
606 | |
---|
607 | Node* node = bucket[b]; |
---|
608 | |
---|
609 | while (node != NULL) { |
---|
610 | if ((node->hashCode == code) && (node->entry.key == key)) { |
---|
611 | return true; |
---|
612 | } |
---|
613 | node = node->next; |
---|
614 | } while (node != NULL); |
---|
615 | |
---|
616 | return false; |
---|
617 | } |
---|
618 | |
---|
619 | |
---|
620 | /** |
---|
621 | Short syntax for get. |
---|
622 | */ |
---|
623 | inline Value& operator[](const Key &key) const { |
---|
624 | return get(key); |
---|
625 | } |
---|
626 | |
---|
627 | |
---|
628 | /** |
---|
629 | Returns an array of all of the keys in the table. |
---|
630 | You can iterate over the keys to get the values. |
---|
631 | */ |
---|
632 | Array<Key> getKeys() const { |
---|
633 | Array<Key> keyArray; |
---|
634 | getKeys(keyArray); |
---|
635 | return keyArray; |
---|
636 | } |
---|
637 | |
---|
638 | void getKeys(Array<Key>& keyArray) const { |
---|
639 | keyArray.resize(0, DONT_SHRINK_UNDERLYING_ARRAY); |
---|
640 | for (size_t i = 0; i < numBuckets; i++) { |
---|
641 | Node* node = bucket[i]; |
---|
642 | while (node != NULL) { |
---|
643 | keyArray.append(node->entry.key); |
---|
644 | node = node->next; |
---|
645 | } |
---|
646 | } |
---|
647 | } |
---|
648 | |
---|
649 | /** |
---|
650 | Calls delete on all of the keys. Does not clear the table, |
---|
651 | however, so you are left with a table of dangling pointers. |
---|
652 | |
---|
653 | Same as <CODE>getKeys().deleteAll();</CODE> |
---|
654 | |
---|
655 | To delete all of the values, you may want something like |
---|
656 | <PRE> |
---|
657 | Array<Key> keys = table.getKeys(); |
---|
658 | Set<Value> value; |
---|
659 | for (int k = 0; k < keys.length(); k++) { |
---|
660 | value.insert(keys[k]); |
---|
661 | } |
---|
662 | value.getMembers().deleteAll(); |
---|
663 | keys.deleteAll(); |
---|
664 | </PRE> |
---|
665 | */ |
---|
666 | void deleteKeys() { |
---|
667 | getKeys().deleteAll(); |
---|
668 | } |
---|
669 | |
---|
670 | /** |
---|
671 | Calls delete on all of the values. This is unsafe-- |
---|
672 | do not call unless you know that each value appears |
---|
673 | at most once. |
---|
674 | |
---|
675 | Does not clear the table, so you are left with a table |
---|
676 | of dangling pointers. |
---|
677 | */ |
---|
678 | void deleteValues() { |
---|
679 | for (int i = 0; i < numBuckets; i++) { |
---|
680 | Node* node = bucket[i]; |
---|
681 | while (node != NULL) { |
---|
682 | delete node->entry.value; |
---|
683 | node = node->next; |
---|
684 | } |
---|
685 | } |
---|
686 | } |
---|
687 | }; |
---|
688 | |
---|
689 | } // namespace |
---|
690 | |
---|
691 | #ifdef G3D_WIN32 |
---|
692 | # pragma warning (pop) |
---|
693 | #endif |
---|
694 | |
---|
695 | #endif |
---|