@@ -15,9 +15,10 @@ package collection
15
15
16
16
import generic ._
17
17
import mutable .Builder
18
- import scala .annotation .migration
19
- import scala .annotation .unchecked .{ uncheckedVariance => uV }
18
+ import scala .annotation .{ migration , tailrec }
19
+ import scala .annotation .unchecked .{uncheckedVariance => uV }
20
20
import parallel .ParIterable
21
+ import scala .collection .immutable .{:: , List , Nil }
21
22
import scala .language .higherKinds
22
23
23
24
/** A template trait for traversable collections of type `Traversable[A]`.
@@ -246,11 +247,95 @@ trait TraversableLike[+A, +Repr] extends Any
246
247
}
247
248
248
249
private [scala] def filterImpl (p : A => Boolean , isFlipped : Boolean ): Repr = {
249
- val b = newBuilder
250
- for (x <- this )
251
- if (p(x) != isFlipped) b += x
250
+ this match {
251
+ case as : List [A ] =>
252
+ filterImplList(as, p, isFlipped).asInstanceOf [Repr ]
253
+ case _ =>
254
+ val b = newBuilder
255
+ for (x <- this )
256
+ if (p(x) != isFlipped) b += x
257
+
258
+ b.result
259
+ }
260
+ }
252
261
253
- b.result
262
+ private [this ] def filterImplList [A ](self : List [A ], p : A => Boolean , isFlipped : Boolean ): List [A ] = {
263
+
264
+ // everything seen so far so far is not included
265
+ @ tailrec def noneIn (l : List [A ]): List [A ] = {
266
+ if (l.isEmpty)
267
+ Nil
268
+ else {
269
+ val h = l.head
270
+ val t = l.tail
271
+ if (p(h) != isFlipped)
272
+ allIn(l, t)
273
+ else
274
+ noneIn(t)
275
+ }
276
+ }
277
+
278
+ // everything from 'start' is included, if everything from this point is in we can return the origin
279
+ // start otherwise if we discover an element that is out we must create a new partial list.
280
+ @ tailrec def allIn (start : List [A ], remaining : List [A ]): List [A ] = {
281
+ if (remaining.isEmpty)
282
+ start
283
+ else {
284
+ val x = remaining.head
285
+ if (p(x) != isFlipped)
286
+ allIn(start, remaining.tail)
287
+ else
288
+ partialFill(start, remaining)
289
+ }
290
+ }
291
+
292
+ // we have seen elements that should be included then one that should be excluded, start building
293
+ def partialFill (origStart : List [A ], firstMiss : List [A ]): List [A ] = {
294
+ val newHead = new :: (origStart.head, Nil )
295
+ var toProcess = origStart.tail
296
+ var currentLast = newHead
297
+
298
+ // we know that all elements are :: until at least firstMiss.tail
299
+ while (! (toProcess eq firstMiss)) {
300
+ val newElem = new :: (toProcess.head, Nil )
301
+ currentLast.tl = newElem
302
+ currentLast = newElem
303
+ toProcess = toProcess.tail
304
+ }
305
+
306
+ // at this point newHead points to a list which is a duplicate of all the 'in' elements up to the first miss.
307
+ // currentLast is the last element in that list.
308
+
309
+ // now we are going to try and share as much of the tail as we can, only moving elements across when we have to.
310
+ var next = firstMiss.tail
311
+ var nextToCopy = next // the next element we would need to copy to our list if we cant share.
312
+ while (! next.isEmpty) {
313
+ // generally recommended is next.isNonEmpty but this incurs an extra method call.
314
+ val head : A = next.head
315
+ if (p(head) != isFlipped) {
316
+ next = next.tail
317
+ } else {
318
+ // its not a match - do we have outstanding elements?
319
+ while (! (nextToCopy eq next)) {
320
+ val newElem = new :: (nextToCopy.head, Nil )
321
+ currentLast.tl = newElem
322
+ currentLast = newElem
323
+ nextToCopy = nextToCopy.tail
324
+ }
325
+ nextToCopy = next.tail
326
+ next = next.tail
327
+ }
328
+ }
329
+
330
+ // we have remaining elements - they are unchanged attach them to the end
331
+ if (! nextToCopy.isEmpty)
332
+ currentLast.tl = nextToCopy
333
+
334
+ newHead
335
+ }
336
+
337
+ val result = noneIn(self)
338
+ result
254
339
}
255
340
256
341
/** Selects all elements of this $coll which satisfy a predicate.
0 commit comments