@@ -156,15 +156,7 @@ private void init(int expectedSize) {
156156
157157 /**
158158 * Finds and removes {@code node} from the key-to-value hash table, the value-to-key hash table,
159- * and the iteration-order chain, leaving its own references to other nodes intact.
160- */
161- /*
162- * TODO: cl/778043974 - Currently, callers are inconsistent about whether they clear the node's
163- * references to other nodes: None clear the bucket-chain fields, but some clear the
164- * iteration-order fields. (Some callers need to be careful not to clear the iteration-order
165- * fields too soon.) Do we need to clear fields at all, given that this Node will no longer be
166- * referenced after it's deleted from the map (except possibly through Entry objects from an
167- * earlier iteration over the map)? If we do, should we be more consistent about it?
159+ * and the iteration-order chain. This includes clearing its own references to other entries.
168160 */
169161 private void delete (Node <K , V > node ) {
170162 int keyBucket = node .keyHash & mask ;
@@ -211,11 +203,49 @@ private void delete(Node<K, V> node) {
211203 node .nextInKeyInsertionOrder .prevInKeyInsertionOrder = node .prevInKeyInsertionOrder ;
212204 }
213205
206+ node .prevInKeyInsertionOrder = null ;
207+ node .nextInKeyInsertionOrder = null ;
208+ node .nextInKToVBucket = null ;
209+ node .nextInVToKBucket = null ;
210+
214211 size --;
215212 modCount ++;
216213 }
217214
218- private void insert (Node <K , V > node , @ Nullable Node <K , V > oldNodeForKey ) {
215+ private void insertPlacingAtEndOfIterationOrder (Node <K , V > node ) {
216+ insertIntoHashBucketsOnly (node );
217+
218+ node .prevInKeyInsertionOrder = lastInKeyInsertionOrder ;
219+ if (lastInKeyInsertionOrder == null ) {
220+ firstInKeyInsertionOrder = node ;
221+ } else {
222+ lastInKeyInsertionOrder .nextInKeyInsertionOrder = node ;
223+ }
224+ lastInKeyInsertionOrder = node ;
225+ }
226+
227+ private void insertSplicingIntoIterationOrder (
228+ Node <K , V > node ,
229+ @ Nullable Node <K , V > prevInKeyInsertionOrder ,
230+ @ Nullable Node <K , V > nextInKeyInsertionOrder ) {
231+ insertIntoHashBucketsOnly (node );
232+
233+ node .prevInKeyInsertionOrder = prevInKeyInsertionOrder ;
234+ if (prevInKeyInsertionOrder == null ) {
235+ firstInKeyInsertionOrder = node ;
236+ } else {
237+ prevInKeyInsertionOrder .nextInKeyInsertionOrder = node ;
238+ }
239+
240+ node .nextInKeyInsertionOrder = nextInKeyInsertionOrder ;
241+ if (nextInKeyInsertionOrder == null ) {
242+ lastInKeyInsertionOrder = node ;
243+ } else {
244+ nextInKeyInsertionOrder .prevInKeyInsertionOrder = node ;
245+ }
246+ }
247+
248+ private void insertIntoHashBucketsOnly (Node <K , V > node ) {
219249 int keyBucket = node .keyHash & mask ;
220250 node .nextInKToVBucket = hashTableKToV [keyBucket ];
221251 hashTableKToV [keyBucket ] = node ;
@@ -224,34 +254,17 @@ private void insert(Node<K, V> node, @Nullable Node<K, V> oldNodeForKey) {
224254 node .nextInVToKBucket = hashTableVToK [valueBucket ];
225255 hashTableVToK [valueBucket ] = node ;
226256
227- if (oldNodeForKey == null ) {
228- node .prevInKeyInsertionOrder = lastInKeyInsertionOrder ;
229- node .nextInKeyInsertionOrder = null ;
230- if (lastInKeyInsertionOrder == null ) {
231- firstInKeyInsertionOrder = node ;
232- } else {
233- lastInKeyInsertionOrder .nextInKeyInsertionOrder = node ;
234- }
235- lastInKeyInsertionOrder = node ;
236- } else {
237- node .prevInKeyInsertionOrder = oldNodeForKey .prevInKeyInsertionOrder ;
238- if (node .prevInKeyInsertionOrder == null ) {
239- firstInKeyInsertionOrder = node ;
240- } else {
241- node .prevInKeyInsertionOrder .nextInKeyInsertionOrder = node ;
242- }
243- node .nextInKeyInsertionOrder = oldNodeForKey .nextInKeyInsertionOrder ;
244- if (node .nextInKeyInsertionOrder == null ) {
245- lastInKeyInsertionOrder = node ;
246- } else {
247- node .nextInKeyInsertionOrder .prevInKeyInsertionOrder = node ;
248- }
249- }
250-
251257 size ++;
252258 modCount ++;
253259 }
254260
261+ private void replaceNodeForKey (Node <K , V > oldNode , Node <K , V > newNode ) {
262+ Node <K , V > prevInKeyInsertionOrder = oldNode .prevInKeyInsertionOrder ;
263+ Node <K , V > nextInKeyInsertionOrder = oldNode .nextInKeyInsertionOrder ;
264+ delete (oldNode ); // clears the two fields we just read
265+ insertSplicingIntoIterationOrder (newNode , prevInKeyInsertionOrder , nextInKeyInsertionOrder );
266+ }
267+
255268 private @ Nullable Node <K , V > seekByKey (@ Nullable Object key , int keyHash ) {
256269 for (Node <K , V > node = hashTableKToV [keyHash & mask ];
257270 node != null ;
@@ -327,13 +340,10 @@ public boolean containsValue(@Nullable Object value) {
327340
328341 Node <K , V > newNode = new Node <>(key , keyHash , value , valueHash );
329342 if (oldNodeForKey != null ) {
330- delete (oldNodeForKey );
331- insert (newNode , oldNodeForKey );
332- oldNodeForKey .prevInKeyInsertionOrder = null ;
333- oldNodeForKey .nextInKeyInsertionOrder = null ;
343+ replaceNodeForKey (oldNodeForKey , newNode );
334344 return oldNodeForKey .value ;
335345 } else {
336- insert (newNode , null );
346+ insertPlacingAtEndOfIterationOrder (newNode );
337347 rehashIfNecessary ();
338348 return null ;
339349 }
@@ -361,31 +371,18 @@ public boolean containsValue(@Nullable Object value) {
361371 throw new IllegalArgumentException ("key already present: " + key );
362372 }
363373
364- /*
365- * The ordering here is important: if we deleted the key node and then the value node, the key
366- * node's prev or next pointer might point to the dead value node, and when we put the new node
367- * in the key node's position in iteration order, it might invalidate the linked list.
368- */
369-
370374 if (oldNodeForValue != null ) {
371375 delete (oldNodeForValue );
372376 }
373377
374- if (oldNodeForKey != null ) {
375- delete (oldNodeForKey );
376- }
377-
378378 Node <K , V > newNode = new Node <>(key , keyHash , value , valueHash );
379- insert (newNode , oldNodeForKey );
380-
381379 if (oldNodeForKey != null ) {
382- oldNodeForKey .prevInKeyInsertionOrder = null ;
383- oldNodeForKey .nextInKeyInsertionOrder = null ;
384- }
385- if (oldNodeForValue != null ) {
386- oldNodeForValue .prevInKeyInsertionOrder = null ;
387- oldNodeForValue .nextInKeyInsertionOrder = null ;
380+ replaceNodeForKey (oldNodeForKey , newNode );
381+ } else {
382+ insertPlacingAtEndOfIterationOrder (newNode );
388383 }
384+
385+ // TODO(cpovirk): Don't perform rehash check if we replaced an existing entry (as in `put`)?
389386 rehashIfNecessary ();
390387 return keyOrNull (oldNodeForValue );
391388 }
@@ -403,7 +400,8 @@ private void rehashIfNecessary() {
403400 for (Node <K , V > node = firstInKeyInsertionOrder ;
404401 node != null ;
405402 node = node .nextInKeyInsertionOrder ) {
406- insert (node , node );
403+ insertSplicingIntoIterationOrder (
404+ node , node .prevInKeyInsertionOrder , node .nextInKeyInsertionOrder );
407405 }
408406 this .modCount ++;
409407 }
@@ -574,11 +572,8 @@ public V setValue(@ParametricNullness V value) {
574572 return value ;
575573 }
576574 checkArgument (seekByValue (value , valueHash ) == null , "value already present: %s" , value );
577- delete (node );
578575 Node <K , V > newNode = new Node <>(node .key , node .keyHash , value , valueHash );
579- insert (newNode , node );
580- node .prevInKeyInsertionOrder = null ;
581- node .nextInKeyInsertionOrder = null ;
576+ replaceNodeForKey (node , newNode );
582577 expectedModCount = modCount ;
583578 if (Objects .equals (toRemove , node )) {
584579 toRemove = newNode ;
@@ -767,7 +762,7 @@ private K setObverseKey(@ParametricNullness K obverseKey) {
767762 obverseKey );
768763 obverse .delete (node );
769764 Node <K , V > newNode = new Node <>(obverseKey , obverseKeyHash , node .value , node .valueHash );
770- obverse .insert (newNode , /* oldNodeForKey= */ null );
765+ obverse .insertPlacingAtEndOfIterationOrder (newNode );
771766 expectedModCount = obverse .modCount ;
772767 K oldObverseKey = node .key ;
773768 if (Objects .equals (toRemove , node )) {
0 commit comments