Skip to content

Checking Size of HashTable

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 (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*)) ;=> 3

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*)) ;=> 2

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.

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)`
  • Use hash-table-count to find out how many items are in the table.
  • Choose the right :test function: This is critical for correctness. Use #'equal for strings.
  • Provide an initial :size if you have a good estimate of the final number of entries. This is a simple and effective optimization.