Skip to content

Use foreach instead of manual iterator loops in Seq.deleted/updatedWith/splitAround - #82

Open
cheeseng wants to merge 1 commit into
dotty-staging:stdlib/seq-editsfrom
artimahub:stdlib/seq-edits-artima-cs
Open

Use foreach instead of manual iterator loops in Seq.deleted/updatedWith/splitAround#82
cheeseng wants to merge 1 commit into
dotty-staging:stdlib/seq-editsfrom
artimahub:stdlib/seq-edits-artima-cs

Conversation

@cheeseng

Copy link
Copy Markdown

deleted, updatedWith, and splitAround each traversed their receiver
via val it = iterator; while (it.hasNext) { ... it.next() ... },
even though none of them rely on iterator-specific capabilities —
every element is visited unconditionally in all three (splitAround's
two-loop shape looks like it needs a resumable iterator, but the same
logic is expressible as a single pass with a boolean flag).

Switch all three to foreach, which dispatches to each collection's
own native traversal (e.g. List walking cons cells directly,
index-based loops for array-backed types) instead of allocating an
Iterator and paying two virtual calls (hasNext/next) per element.

Additional changes:

  • deleted: add b.sizeHint(this, -1). The result size is always known
    exactly once a valid index is found (source size minus one), so the
    builder can avoid backing-array resizes as elements are added.
  • updatedWith: add b.sizeHint(this), optimistically sized for the
    common case where f returns Some (result size == source size).
    sizeHint is documented to tolerate being wrong, so a None at the
    matched index only costs a harmless one-element overshoot. Also
    replaced the manual f(a) match { case Some(v) => b += v; case None => } with f(a).foreach(b += _), matching Option.foreach
    usage elsewhere in this file.
  • splitAround: rewritten as a single pass driven by a switched
    boolean instead of two sequential iterator loops. No sizeHint added
    here, since the split position is data-dependent and there's no
    reliable size estimate available for either builder in advance.

No behavior changes; existing tests should cover this as-is.

All three methods traversed via a manual iterator/while loop even
though none of them need iterator-specific capabilities (resumable
position, early termination) — every element is visited regardless.
Switch them to foreach, which dispatches to each collection's native
traversal (e.g. List walking cons cells, index-based loops for
array-backed types) instead of paying for an Iterator allocation plus
two virtual calls (hasNext/next) per element.

- deleted: also add b.sizeHint(this, -1), since the result size is
  always known exactly (one fewer than the source) when a valid index
  is found.
- updatedWith: also add b.sizeHint(this), optimistically assuming the
  common case where f returns Some (same size as source); sizeHint is
  documented to tolerate being wrong, so a None at the matched index
  just means a harmless one-off overshoot. Also replace the manual
  Some/None match with f(a).foreach(b += _), matching the idiom used
  elsewhere.
- splitAround: rewritten as a single boolean-flag state machine
  (switched) instead of two sequential iterator loops. No sizeHint
  added here — the split point is data-dependent, so neither builder
  has a reliable size estimate in advance.

No behavior changes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant