Checking Size of HashTable
Modern Lisp: Hash Table Size and Capacity
Section titled “Modern Lisp: Hash Table Size and Capacity”Hash tables are a fundamental and powerful data structure in Common Lisp, used for creating efficient key-value mappings. When working with them, it’s crucial to understand the difference between the number of entries and the underlying capacity.
Count vs. Capacity: The Core Distinction
Section titled “Count vs. Capacity: The Core Distinction”- Count (
hash-table-count): This tells you how many key-value pairs are currently stored in the hash table. This is the value you’ll use most often. - Size/Capacity (
hash-table-size): This tells you the internal capacity of the hash table—how many entries it can hold before it needs to be resized. This is primarily a tool for performance tuning.
Think of a parking garage. hash-table-count is the number of cars currently parked. hash-table-size is the total number of parking spots available.
Getting the Number of Entries: hash-table-count
Section titled “Getting the Number of Entries: hash-table-count”This function returns the number of key-value pairs in the table.
;; Create an empty hash table(defvar *user-cache* (make-hash-table))
(print (hash-table-count *user-cache*)) ;=> 0
;; Add some entries(setf (gethash :user-1 *user-cache*) "Alice")(setf (gethash :user-2 *user-cache*) "Bob")
(print (hash-table-count *user-cache*)) ;=> 2
;; Add one more(setf (gethash :user-3 *user-cache*) "Charlie")
(print (hash-table-count *user-cache*)) ;=> 3Getting the Capacity: hash-table-size
Section titled “Getting the Capacity: hash-table-size”This function reveals the internal allocated size of the hash table. The exact value is implementation-dependent, but it will be a number at least as large as the initial :size you requested.
;; Create a hash table, suggesting an initial size(defvar *my-ht* (make-hash-table :size 50))
;; The actual capacity is implementation-dependent but should be >= 50(print (hash-table-size *my-ht*)) ;=> e.g., 65 (in SBCL)
;; The count is still zero(print (hash-table-count *my-ht*)) ;=> 0
;; Add some entries(setf (gethash :a *my-ht*) 1)(setf (gethash :b *my-ht*) 2)
;; The capacity remains the same, but the count has changed(print (hash-table-size *my-ht*)) ;=> e.g., 65(print (hash-table-count *my-ht*)) ;=> 2Performance Tuning with make-hash-table
Section titled “Performance Tuning with make-hash-table”You can influence a hash table’s performance and memory usage by providing keywords to make-hash-table.
- :test: The most important parameter. It specifies the comparison function for keys. The default is
eql. Choosing the wrong test is a common source of bugs. -
- `#'eq` (fastest): For comparing symbols or identical objects.
-
- `#'eql` (default): For symbols and numbers.
-
- `#'equal`: For strings, lists, and other structures that are structurally similar.
-
- `#'equalp` (slowest): Like `equal`, but case-insensitive for strings and characters.
- :size: A hint to the implementation about the initial number of entries you expect to store. Setting this can prevent early resizes.
- :rehash-size & :rehash-threshold: Control how and when the table grows. The threshold determines how “full” the table gets before resizing. The defaults are usually fine.
Common Pitfall: The Key Test Mismatch
Section titled “Common Pitfall: The Key Test Mismatch”A frequent error is trying to retrieve a value using a key that isn’t equivalent under the table’s :test function.
;; Create a table with the default test, 'eql'(defvar *ht* (make-hash-table))
;; Add an entry with a string key(setf (gethash "my-key" *ht*) "my-value")
;; Retrieve it with an `equal` string. This works because 'eql';; for strings falls back to 'equal' if they are not 'eq'.;; But this is a subtle point, 'equal' is the correct test for strings.(gethash "my-key" *ht*) ;=> "my-value", T
;; --- The Classic Bug ---
;; Create a table with the 'eq' test(defvar *bad-ht* (make-hash-table :test #'eq))
;; Add an entry with a string key(setf (gethash "my-key" *bad-ht*) "my-value")
;; Try to retrieve it with a different string object that has the same content(gethash "my-key" *bad-ht*) ;=> NIL, NIL (!!)
;; Why? Because the two "my-key" strings are not `eq` (they are not the same object in memory).;; The correct way to create this table is `(make-hash-table :test #'equal)`Best Practices
Section titled “Best Practices”- Use
hash-table-countto find out how many items are in the table. - Choose the right
:testfunction: This is critical for correctness. Use#'equalfor strings. - Provide an initial
:sizeif you have a good estimate of the final number of entries. This is a simple and effective optimization.