22
33import java .util .HashMap ;
44import java .util .Map ;
5- import java .util .Objects ;
65
76/**
87 * Patricia (radix) trie for String keys and generic values.
98 *
10- * <p>Edges are compressed : each child edge stores a non-empty String label.
11- * Operations run in O(L) where L is the key length.</p>
9+ * <p>Compressed edges : each child edge stores a non-empty String label.
10+ * Operations are O(L) where L is the key length.</p>
1211 *
13- * <p>Contract :
12+ * <p>Contracts :
1413 * <ul>
1514 * <li>Null keys are not allowed (IllegalArgumentException).</li>
1615 * <li>Empty-string key ("") is allowed as a valid key.</li>
@@ -76,39 +75,28 @@ public boolean contains(String key) {
7675 }
7776
7877 /**
79- * Removes the mapping for {@code key} if present.
78+ * Removes {@code key} if present.
8079 *
81- * <p>Fixes for CI failures:
82- * - Properly removes leaf nodes and decrements size once.
83- * - Merges redundant pass-through nodes (no value, single child) by
84- * concatenating edge labels.</p>
80+ * <p>Also compacts pass-through nodes (no value + single child) by concatenating
81+ * edge labels to keep the trie compressed.</p>
8582 *
8683 * @param key non-null key
87- * @return previous value or {@code null} if none
84+ * @return true if the key existed and was removed
8885 */
89- public V remove (String key ) {
86+ public boolean remove (String key ) {
9087 if (key == null ) {
91- throw new IllegalArgumentException ("key cannot be null" );
88+ throw new IllegalArgumentException ("key must not be null" );
9289 }
9390 if (key .isEmpty ()) {
9491 if (!root .hasValue ) {
95- return null ;
92+ return false ;
9693 }
97- V old = root .value ;
9894 root .hasValue = false ;
9995 root .value = null ;
10096 size --;
101- return old ;
97+ return true ;
10298 }
103-
104- // container to return "was removed" + old value up the recursion
105- Object [] removedHolder = new Object [1 ];
106- removeRecursive (root , key , removedHolder );
107-
108- if (removedHolder [0 ] != null ) {
109- size --;
110- }
111- return (V ) removedHolder [0 ];
99+ return removeRecursive (root , key );
112100 }
113101
114102 /**
@@ -269,9 +257,10 @@ private Node<V> findPrefixNode(Node<V> node, String prefix) {
269257 * </ul>
270258 * </p>
271259 */
272- private void removeRecursive (Node <V > parent , String key , Object [] removedHolder ) {
260+ private boolean removeRecursive (Node <V > parent , String key ) {
273261 // iterate on a snapshot of keys to allow modifications during loop
274- for (String edge : parent .children .keySet ().toArray (new String [0 ])) {
262+ String [] keys = parent .children .keySet ().toArray (new String [0 ]);
263+ for (String edge : keys ) {
275264 int cpl = commonPrefixLen (edge , key );
276265 if (cpl == 0 ) {
277266 continue ;
@@ -281,20 +270,25 @@ private void removeRecursive(Node<V> parent, String key, Object[] removedHolder)
281270
282271 // partial overlap with edge => key doesn't exist in this branch
283272 if (cpl < edge .length ()) {
284- return ;
273+ return false ;
285274 }
286275
287276 String rest = key .substring (cpl );
277+ boolean removed ;
288278 if (rest .isEmpty ()) {
289- // we've reached the node that holds the key
290- if (child .hasValue ) {
291- removedHolder [0 ] = child .value ;
292- child .hasValue = false ;
293- child .value = null ;
279+ if (!child .hasValue ) {
280+ return false ;
294281 }
282+ child .hasValue = false ;
283+ child .value = null ;
284+ size --;
285+ removed = true ;
295286 } else {
296- // keep traversing
297- removeRecursive (child , rest , removedHolder );
287+ removed = removeRecursive (child , rest );
288+ }
289+
290+ if (!removed ) {
291+ return false ;
298292 }
299293
300294 // post-recursion cleanup of child
@@ -314,8 +308,9 @@ private void removeRecursive(Node<V> parent, String key, Object[] removedHolder)
314308 parent .children .put (edge + grandEdge , grand );
315309 }
316310 }
317- return ; // processed the matching path
311+ return true ; // processed the matching path
318312 }
313+ return false ;
319314 }
320315
321316 /** Length of common prefix of a and b. */
@@ -327,14 +322,4 @@ private static int commonPrefixLen(String a, String b) {
327322 }
328323 return i ;
329324 }
330-
331- @ Override
332- public int hashCode () {
333- return Objects .hash (size );
334- }
335-
336- @ Override
337- public boolean equals (Object obj ) {
338- return this == obj ;
339- }
340325}
0 commit comments