Some workarounds might be:
If the common pattern is lookup -> find-it -> overwrite-it, then you could replace the value type to a list that contains the value type. Then after finding the value object for the key, just destructively replace its first element, e.g.
(defun try-add (i)
(let ((old-i-list (gethash complex-list table nil)))
(if (may-add (first old-i-list))
(setf (first old-i-list) i) ; overwrite without searching again
(setf (gethash complex-list table) (list i))))) ; not there? too bad, we have to gethash again
Alternatively, if the common pattern is more like lookup -> it's-not-there -> add-it, you might want to consider hashing the keys on your own, and then have the hash table use your hashed value as the key. This might be more complicated, depending on the depth and semantics of these complex lists. In the simple case, you might get away with a hash function that (recursively) xor's the hash value of the elements of its list argument.
EDITED: answering the question in the comments: the idea is that instead of the hash table mapping keys to values, the hash table will now map keys to single element lists, where the element is the value. Then you can change the contents of these lists without touching the hash table itself. The following is from SBCL:
* (defparameter *my-hash* (make-hash-table))
*MY-HASH*
* (setf (gethash :my-key *my-hash*) (list "old-value"))
("old-value")
* (gethash :my-key *my-hash*)
("old-value")
T
* (defparameter old-value-container (gethash :my-key *my-hash*))
OLD-VALUE-CONTAINER
* (setf (first old-value-container) "new value")
"new value"
* (gethash :my-key *my-hash*)
("new value")
T