@@ -438,17 +438,101 @@ sealed abstract class List[+A]
438
438
loop(null , null , this , this )
439
439
}
440
440
441
+ override def filter (p : A => Boolean ): List [A ] = filterImpl(p, isFlipped = false )
442
+
443
+ override def filterNot (p : A => Boolean ): List [A ] = filterImpl(p, isFlipped = true )
444
+
445
+ private [this ] def filterImpl (p : A => Boolean , isFlipped : Boolean ): List [A ] = {
446
+
447
+ // everything seen so far so far is not included
448
+ @ tailrec def noneIn (l : List [A ]): List [A ] = {
449
+ if (l.isEmpty)
450
+ Nil
451
+ else {
452
+ val h = l.head
453
+ val t = l.tail
454
+ if (p(h) != isFlipped)
455
+ allIn(l, t)
456
+ else
457
+ noneIn(t)
458
+ }
459
+ }
460
+
461
+ // everything from 'start' is included, if everything from this point is in we can return the origin
462
+ // start otherwise if we discover an element that is out we must create a new partial list.
463
+ @ tailrec def allIn (start : List [A ], remaining : List [A ]): List [A ] = {
464
+ if (remaining.isEmpty)
465
+ start
466
+ else {
467
+ val x = remaining.head
468
+ if (p(x) != isFlipped)
469
+ allIn(start, remaining.tail)
470
+ else
471
+ partialFill(start, remaining)
472
+ }
473
+ }
474
+
475
+ // we have seen elements that should be included then one that should be excluded, start building
476
+ def partialFill (origStart : List [A ], firstMiss : List [A ]): List [A ] = {
477
+ val newHead = new :: (origStart.head, Nil )
478
+ var toProcess = origStart.tail
479
+ var currentLast = newHead
480
+
481
+ // we know that all elements are :: until at least firstMiss.tail
482
+ while (! (toProcess eq firstMiss)) {
483
+ val newElem = new :: (toProcess.head, Nil )
484
+ currentLast.next = newElem
485
+ currentLast = newElem
486
+ toProcess = toProcess.tail
487
+ }
488
+
489
+ // at this point newHead points to a list which is a duplicate of all the 'in' elements up to the first miss.
490
+ // currentLast is the last element in that list.
491
+
492
+ // now we are going to try and share as much of the tail as we can, only moving elements across when we have to.
493
+ var next = firstMiss.tail
494
+ var nextToCopy = next // the next element we would need to copy to our list if we cant share.
495
+ while (! next.isEmpty) {
496
+ // generally recommended is next.isNonEmpty but this incurs an extra method call.
497
+ val head : A = next.head
498
+ if (p(head) != isFlipped) {
499
+ next = next.tail
500
+ } else {
501
+ // its not a match - do we have outstanding elements?
502
+ while (! (nextToCopy eq next)) {
503
+ val newElem = new :: (nextToCopy.head, Nil )
504
+ currentLast.next = newElem
505
+ currentLast = newElem
506
+ nextToCopy = nextToCopy.tail
507
+ }
508
+ nextToCopy = next.tail
509
+ next = next.tail
510
+ }
511
+ }
512
+
513
+ // we have remaining elements - they are unchanged attach them to the end
514
+ if (! nextToCopy.isEmpty)
515
+ currentLast.next = nextToCopy
516
+
517
+ newHead
518
+ }
519
+
520
+ noneIn(this )
521
+ }
522
+
441
523
final override def toList : List [A ] = this
442
524
443
525
// Override for performance
444
526
override def equals (o : scala.Any ): Boolean = {
445
527
@ tailrec def listEq (a : List [_], b : List [_]): Boolean =
446
528
(a eq b) || {
447
- if (a.nonEmpty && b.nonEmpty && a.head == b.head) {
529
+ val aEmpty = a.isEmpty
530
+ val bEmpty = b.isEmpty
531
+ if (! (aEmpty || bEmpty) && a.head == b.head) {
448
532
listEq(a.tail, b.tail)
449
533
}
450
534
else {
451
- a.isEmpty && b.isEmpty
535
+ aEmpty && bEmpty
452
536
}
453
537
}
454
538
0 commit comments