             overwrite? (.hasOwnProperty new-strobj k)]
         (aset new-strobj k v)
         (if overwrite?
-          (HashMap. meta keys new-strobj hashobj)
+          (ObjMap. meta keys new-strobj)     ; overwrite
           (let [new-keys (array-clone keys)] ; append
             (.push new-keys k)
-            (HashMap. meta new-keys new-strobj hashobj))))
-      ; non-string key
-      (let [h (hash k)
-            bucket (aget hashobj h)]
-        (if bucket
-          (let [new-bucket (array-clone bucket)
-                new-hashobj (goog.object/clone hashobj)]
-            (aset new-hashobj h new-bucket)
-            (if-let [i (scan-array 2 k new-bucket)]
-              (do
-                (aset new-bucket (inc i) v) ; found key, replace
-                (HashMap. meta keys strobj new-hashobj))
-              (let [new-keys (array-clone keys)] ; did not find key, append
-                (.push new-keys k)
-                (.push new-bucket k v)
-                (HashMap. meta new-keys strobj new-hashobj))))
-          (let [new-keys (array-clone keys)
-                new-hashobj (goog.object/clone hashobj)]
-            (.push new-keys k)
-            (aset new-hashobj h (array k v))
-            (HashMap. meta new-keys strobj new-hashobj))))))
-  
+            (ObjMap. meta new-keys new-strobj))))
+      ; non-string key. game over.
+      (with-meta (into (hash-map k v) (seq coll)) meta)))
+
   IMap
   (-dissoc [coll k]
-    (if (goog.isString k)
-      (if (not (.hasOwnProperty strobj k))
-        coll ; key not found, return coll unchanged
-        (let [new-keys (array-clone keys)
-              new-strobj (goog.object/clone strobj)
-              new-count (dec (.length keys))]
-          (.splice new-keys (scan-array 1 k new-keys) 1)
-          (js-delete new-strobj k)
-          (HashMap. meta new-keys new-strobj hashobj)))
-      ; non-string key
-      (let [h (hash k)
-            bucket (aget hashobj h)
-            i (when bucket (scan-array 2 k bucket))]
-        (if (not i)
-          coll ; key not found, return coll unchanged
-          (let [new-keys (array-clone keys)
-                new-hashobj (goog.object/clone hashobj)]
-            (if (> 3 (.length bucket))
-              (js-delete new-hashobj h)
-              (let [new-bucket (array-clone bucket)]
-                (.splice new-bucket i 2)
-                (aset new-hashobj h new-bucket)))
-            (.splice new-keys (scan-array 1 k new-keys) 1)
-            (HashMap. meta new-keys strobj new-hashobj))))))
+    (if (and (goog/isString k) (.hasOwnProperty strobj k))
+      (let [new-keys (array-clone keys)
+            new-strobj (goog.object/clone strobj)]
+        (.splice new-keys (scan-array 1 k new-keys) 1)
+        (js-delete new-strobj k)
+        (HashMap. meta new-keys new-strobj hashobj))
+      coll)) ; key not found, return coll unchanged
 
   IPrintable
   (-pr-seq [coll opts]
     (let [pr-pair (fn [keyval] (pr-sequential pr-seq "" " " "" opts keyval))]
       (pr-sequential pr-pair "{" ", " "}" opts coll))))
 
-(set! cljs.core.HashMap/EMPTY (HashMap. nil (array) (js-obj) (js-obj)))
+; The keys field is an array of all keys of this map, in no particular
+; order. Each key is hashed and the result used as a property name of
+; hashobj. Each values in hashobj is actually a bucket in order to handle hash
+; collisions. A bucket is an array of alternating keys (not their hashes) and
+; vals.
+(deftype HashMap [meta count hashobj]
+  IWithMeta
+  (-with-meta [coll meta] (HashMap. meta count hashobj))
+
+  IMeta
+  (-meta [coll] meta)
+
+  ICollection
+  (-conj [coll entry] (-assoc coll (-nth entry 0) (-nth entry 1)))
+
+; IEmptyableCollection
+; (iempty [coll] coll)
+
+  IEquiv
+  (-equiv [coll other] (equiv-map coll other))
+
+  IHash
+  (-hash [coll] (hash-coll coll))
+
+  ISeqable
+  (-seq [coll]
+    (when (pos? count)
+      (let [hashes (js-keys hashobj)]
+        (mapcat #(map vec (partition 2 (aget hashobj %)))
+                hashes))))
+
+  ICounted
+  (-count [coll] count)
+
+  ILookup
+  (-lookup [coll k] (-lookup coll k nil))
+  (-lookup [coll k not-found]
+    (let [h (hash k)
+          bucket (aget hashobj h)
+          i (when bucket (scan-array 2 k bucket))]
+      (if i
+        (aget bucket (inc i))
+        not-found)))
+
+  IAssociative
+  (-assoc [coll k v]
+    (let [h (hash k)
+          bucket (aget hashobj h)]
+      (if bucket
+        (let [new-bucket (array-clone bucket)
+              new-hashobj (goog.object/clone hashobj)]
+          (aset new-hashobj h new-bucket)
+          (if-let [i (scan-array 2 k new-bucket)]
+            (do ; found key, replace
+              (aset new-bucket (inc i) v)
+              (HashMap. meta count new-hashobj))
+            (do ; did not find key, append
+              (.push new-bucket k v)
+              (HashMap. meta (inc count) new-hashobj))))
+        (let [new-hashobj (goog.object/clone hashobj)] ; did not find bucket
+          (aset new-hashobj h (array k v))
+          (HashMap. meta (inc count) new-hashobj)))))
+  
+  IMap
+  (-dissoc [coll k]
+    (let [h (hash k)
+          bucket (aget hashobj h)
+          i (when bucket (scan-array 2 k bucket))]
+      (if (not i)
+        coll ; key not found, return coll unchanged
+        (let [new-hashobj (goog.object/clone hashobj)]
+          (if (> 3 (.length bucket))
+            (js-delete new-hashobj h)
+            (let [new-bucket (array-clone bucket)]
+              (.splice new-bucket i 2)
+              (aset new-hashobj h new-bucket)))
+          (HashMap. meta (dec count) new-hashobj)))))
+
+  IPrintable
+  (-pr-seq [coll opts]
+    (let [pr-pair (fn [keyval] (pr-sequential pr-seq "" " " "" opts keyval))]
+      (pr-sequential pr-pair "{" ", " "}" opts coll))))
+
+(set! cljs.core.HashMap/EMPTY (HashMap. nil 0 (js-obj)))
 
 (defn hash-map
   "keyval => key val

  [to from]
  (reduce -conj to from))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Printing ;;;;;;;;;;;;;;;;

(defn pr-sequential [print-one begin sep end opts coll]

  [& keyvals]
  (loop [in (seq keyvals), out cljs.core.HashMap/EMPTY]
    (if in
      (recur (nnext in) (assoc out (first in) (second in)))
      out)))

(defn conj
