Skip to content

Implement Iterator.remove() on OptimizedTagMap view iterators#11326

Closed
dougqh wants to merge 1 commit intomasterfrom
dougqh/tagmap-iterator-remove
Closed

Implement Iterator.remove() on OptimizedTagMap view iterators#11326
dougqh wants to merge 1 commit intomasterfrom
dougqh/tagmap-iterator-remove

Conversation

@dougqh
Copy link
Copy Markdown
Contributor

@dougqh dougqh commented May 8, 2026

Summary

The view iterators returned by OptimizedTagMap.entrySet().iterator(), keySet().iterator(), values().iterator(), and the TagMap.iterator() overload all extend IteratorBase, which inherited the default Iterator.remove() that throws UnsupportedOperationException. As a result, AbstractSet/AbstractCollection bulk operations that rely on iterator-remove (removeAll, removeIf, retainAll) silently failed mid-iteration on optimized maps — a Map contract gap.

This patch tracks the last-returned entry on IteratorBase and removes by tag through OptimizedTagMap.getAndRemove, which enforces the frozen check. The fix is contained entirely in IteratorBase, so all three subclasses (EntryReaderIterator, KeysIterator, ValuesIterator) inherit remove() automatically.

LegacyTagMap is unaffected: its view iterators are inherited from HashMap and already support remove. (Its TagMap-level iterator() does not, but that's a separate divergence — see follow-up work below.)

Test plan

  • New tests in TagMapTest:
    • entrySet_removeIf (parameterized over OPTIMIZED + LEGACY)
    • keySet_removeAll (parameterized over OPTIMIZED + LEGACY)
    • values_removeIf (parameterized over OPTIMIZED + LEGACY)
    • optimizedIterator_remove
    • optimizedIterator_remove_acrossBucketCollisions (forces BucketGroup chains, then removes all entries via iterator)
    • optimizedIterator_remove_beforeNext_throws
    • optimizedIterator_remove_twice_throws
    • optimizedIterator_remove_onFrozenMap_throws
  • ./gradlew :internal-api:test passes
  • ./gradlew :internal-api:spotlessCheck passes
  • CI matrix

Follow-up not in scope

  • LegacyTagMap.IteratorImpl.remove() is also unsupported (would need to delegate to the wrapped HashMap iterator). Different mechanism, different PR.
  • Pre-existing class-init ordering issue: running a single @EnumSource(TagMapType.class) test in isolation (e.g. --tests TagMapTest.numericZeroToBooleanCoercion) fails with NPE in TagMap.<clinit> because TagMapType loads OptimizedTagMapFactory.INSTANCE before TagMap. Reproduces on master without these changes; orthogonal to this PR.

🤖 Generated with Claude Code

The view iterators returned by OptimizedTagMap.entrySet().iterator(),
keySet().iterator(), values().iterator(), and the TagMap.iterator()
overload all extend IteratorBase, which inherited the default
Iterator.remove() that throws UnsupportedOperationException. As a
result, AbstractSet/AbstractCollection bulk operations that rely on
iterator-remove (removeAll, removeIf, retainAll) failed mid-iteration
on optimized TagMaps.

Track the last-returned entry on IteratorBase and remove by tag through
OptimizedTagMap.getAndRemove, which enforces the frozen check.
LegacyTagMap is unaffected; its view iterators are inherited from
HashMap and already support remove.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dougqh dougqh added comp: core Tracer core tag: ai generated Largely based on code generated by an AI or LLM type: bug Bug report and fix labels May 8, 2026
@dougqh
Copy link
Copy Markdown
Contributor Author

dougqh commented May 8, 2026

Experiment - based on a Claude review, not quite what I had in mind
I actually wanted to just make a change that forbids removal through Iterators altogether

@dougqh
Copy link
Copy Markdown
Contributor Author

dougqh commented May 8, 2026

Decided this isn't worth pursuing, would be better to just eliminate LegacyTagMap

@dougqh dougqh closed this May 8, 2026
@dougqh dougqh deleted the dougqh/tagmap-iterator-remove branch May 8, 2026 15:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp: core Tracer core tag: ai generated Largely based on code generated by an AI or LLM type: bug Bug report and fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant