scala.collection.immutable.List.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scala-library Show documentation
Show all versions of scala-library Show documentation
Standard library for the Scala Programming Language
The newest version!
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/
package scala
package collection
package immutable
import scala.annotation.unchecked.uncheckedVariance
import scala.annotation.tailrec
import mutable.{Builder, ListBuffer}
import scala.collection.generic.DefaultSerializable
import scala.runtime.Statics.releaseFence
/** A class for immutable linked lists representing ordered collections
* of elements of type `A`.
*
* This class comes with two implementing case classes `scala.Nil`
* and `scala.::` that implement the abstract members `isEmpty`,
* `head` and `tail`.
*
* This class is optimal for last-in-first-out (LIFO), stack-like access patterns. If you need another access
* pattern, for example, random access or FIFO, consider using a collection more suited to this than `List`.
*
* ==Performance==
* '''Time:''' `List` has `O(1)` prepend and head/tail access. Most other operations are `O(n)` on the number of elements in the list.
* This includes the index-based lookup of elements, `length`, `append` and `reverse`.
*
* '''Space:''' `List` implements '''structural sharing''' of the tail list. This means that many operations are either
* zero- or constant-memory cost.
* {{{
* val mainList = List(3, 2, 1)
* val with4 = 4 :: mainList // re-uses mainList, costs one :: instance
* val with42 = 42 :: mainList // also re-uses mainList, cost one :: instance
* val shorter = mainList.tail // costs nothing as it uses the same 2::1::Nil instances as mainList
* }}}
*
* @example {{{
* // Make a list via the companion object factory
* val days = List("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")
*
* // Make a list element-by-element
* val when = "AM" :: "PM" :: Nil
*
* // Pattern match
* days match {
* case firstDay :: otherDays =>
* println("The first day of the week is: " + firstDay)
* case Nil =>
* println("There don't seem to be any week days.")
* }
* }}}
*
* @note The functional list is characterized by persistence and structural sharing, thus offering considerable
* performance and space consumption benefits in some scenarios if used correctly.
* However, note that objects having multiple references into the same functional list (that is,
* objects that rely on structural sharing), will be serialized and deserialized with multiple lists, one for
* each reference to it. I.e. structural sharing is lost after serialization/deserialization.
*
* @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-immutable-collection-classes.html#lists "Scala's Collection Library overview"]]
* section on `Lists` for more information.
*
* @define coll list
* @define Coll `List`
* @define orderDependent
* @define orderDependentFold
* @define mayNotTerminateInf
* @define willNotTerminateInf
*/
@SerialVersionUID(3L)
sealed abstract class List[+A]
extends AbstractSeq[A]
with LinearSeq[A]
with LinearSeqOps[A, List, List[A]]
with StrictOptimizedLinearSeqOps[A, List, List[A]]
with StrictOptimizedSeqOps[A, List, List[A]]
with IterableFactoryDefaults[A, List]
with DefaultSerializable {
override def iterableFactory: SeqFactory[List] = List
/** Adds an element at the beginning of this list.
* @param elem the element to prepend.
* @return a list which contains `x` as first element and
* which continues with this list.
* Example:
* {{{1 :: List(2, 3) = List(2, 3).::(1) = List(1, 2, 3)}}}
*/
def :: [B >: A](elem: B): List[B] = new ::(elem, this)
/** Adds the elements of a given list in front of this list.
*
* Example:
* {{{List(1, 2) ::: List(3, 4) = List(3, 4).:::(List(1, 2)) = List(1, 2, 3, 4)}}}
*
* @param prefix The list elements to prepend.
* @return a list resulting from the concatenation of the given
* list `prefix` and this list.
*/
def ::: [B >: A](prefix: List[B]): List[B] =
if (isEmpty) prefix
else if (prefix.isEmpty) this
else {
val result = new ::[B](prefix.head, this)
var curr = result
var that = prefix.tail
while (!that.isEmpty) {
val temp = new ::[B](that.head, this)
curr.next = temp
curr = temp
that = that.tail
}
releaseFence()
result
}
/** Adds the elements of a given list in reverse order in front of this list.
* `xs reverse_::: ys` is equivalent to
* `xs.reverse ::: ys` but is more efficient.
*
* @param prefix the prefix to reverse and then prepend
* @return the concatenation of the reversed prefix and the current list.
*/
def reverse_:::[B >: A](prefix: List[B]): List[B] = {
var these: List[B] = this
var pres = prefix
while (!pres.isEmpty) {
these = pres.head :: these
pres = pres.tail
}
these
}
override final def isEmpty: Boolean = this eq Nil
override def prepended[B >: A](elem: B): List[B] = elem :: this
override def prependedAll[B >: A](prefix: collection.IterableOnce[B]): List[B] = prefix match {
case xs: List[B] => xs ::: this
case _ if prefix.knownSize == 0 => this
case b: ListBuffer[B] if this.isEmpty => b.toList
case _ =>
val iter = prefix.iterator
if (iter.hasNext) {
val result = new ::[B](iter.next(), this)
var curr = result
while (iter.hasNext) {
val temp = new ::[B](iter.next(), this)
curr.next = temp
curr = temp
}
releaseFence()
result
} else {
this
}
}
// When calling appendAll with another list `suffix`, avoid copying `suffix`
override def appendedAll[B >: A](suffix: collection.IterableOnce[B]): List[B] = suffix match {
case xs: List[B] => this ::: xs
case _ => super.appendedAll(suffix)
}
override def take(n: Int): List[A] = if (isEmpty || n <= 0) Nil else {
val h = new ::(head, Nil)
var t = h
var rest = tail
var i = 1
while ({if (rest.isEmpty) return this; i < n}) {
i += 1
val nx = new ::(rest.head, Nil)
t.next = nx
t = nx
rest = rest.tail
}
releaseFence()
h
}
/** @inheritdoc
*
* @example {{{
* // Given a list
* val letters = List('a','b','c','d','e')
*
* // `slice` returns all elements beginning at index `from` and afterwards,
* // up until index `until` (excluding index `until`.)
* letters.slice(1,3) // Returns List('b','c')
* }}}
*/
override def slice(from: Int, until: Int): List[A] = {
val lo = scala.math.max(from, 0)
if (until <= lo || isEmpty) Nil
else this drop lo take (until - lo)
}
override def takeRight(n: Int): List[A] = {
@tailrec
def loop(lead: List[A], lag: List[A]): List[A] = lead match {
case Nil => lag
case _ :: tail => loop(tail, lag.tail)
}
loop(drop(n), this)
}
// dropRight is inherited from LinearSeq
override def splitAt(n: Int): (List[A], List[A]) = {
val b = new ListBuffer[A]
var i = 0
var these = this
while (!these.isEmpty && i < n) {
i += 1
b += these.head
these = these.tail
}
(b.toList, these)
}
override def updated[B >: A](index: Int, elem: B): List[B] = {
var i = 0
var current = this
val prefix = ListBuffer.empty[B]
while (i < index && current.nonEmpty) {
i += 1
prefix += current.head
current = current.tail
}
if (i == index && current.nonEmpty) {
prefix.prependToList(elem :: current.tail)
} else {
throw new IndexOutOfBoundsException(s"$index is out of bounds (min 0, max ${length-1})")
}
}
final override def map[B](f: A => B): List[B] = {
if (this eq Nil) Nil else {
val h = new ::[B](f(head), Nil)
var t: ::[B] = h
var rest = tail
while (rest ne Nil) {
val nx = new ::(f(rest.head), Nil)
t.next = nx
t = nx
rest = rest.tail
}
releaseFence()
h
}
}
final override def collect[B](pf: PartialFunction[A, B]): List[B] = {
if (this eq Nil) Nil else {
var rest = this
var h: ::[B] = null
var x: Any = null
// Special case for first element
while (h eq null) {
x = pf.applyOrElse(rest.head, List.partialNotApplied)
if (x.asInstanceOf[AnyRef] ne List.partialNotApplied) h = new ::(x.asInstanceOf[B], Nil)
rest = rest.tail
if (rest eq Nil) return if (h eq null) Nil else h
}
var t = h
// Remaining elements
while (rest ne Nil) {
x = pf.applyOrElse(rest.head, List.partialNotApplied)
if (x.asInstanceOf[AnyRef] ne List.partialNotApplied) {
val nx = new ::(x.asInstanceOf[B], Nil)
t.next = nx
t = nx
}
rest = rest.tail
}
releaseFence()
h
}
}
final override def flatMap[B](f: A => IterableOnce[B]): List[B] = {
var rest = this
var h: ::[B] = null
var t: ::[B] = null
while (rest ne Nil) {
val it = f(rest.head).iterator
while (it.hasNext) {
val nx = new ::(it.next(), Nil)
if (t eq null) {
h = nx
} else {
t.next = nx
}
t = nx
}
rest = rest.tail
}
if (h eq null) Nil else {releaseFence(); h}
}
@inline final override def takeWhile(p: A => Boolean): List[A] = {
val b = new ListBuffer[A]
var these = this
while (!these.isEmpty && p(these.head)) {
b += these.head
these = these.tail
}
b.toList
}
@inline final override def span(p: A => Boolean): (List[A], List[A]) = {
val b = new ListBuffer[A]
var these = this
while (!these.isEmpty && p(these.head)) {
b += these.head
these = these.tail
}
(b.toList, these)
}
// Overridden with an implementation identical to the inherited one (at this time)
// solely so it can be finalized and thus inlinable.
@inline final override def foreach[U](f: A => U): Unit = {
var these = this
while (!these.isEmpty) {
f(these.head)
these = these.tail
}
}
final override def reverse: List[A] = {
var result: List[A] = Nil
var these = this
while (!these.isEmpty) {
result = these.head :: result
these = these.tail
}
result
}
final override def foldRight[B](z: B)(op: (A, B) => B): B = {
var acc = z
var these: List[A] = reverse
while (!these.isEmpty) {
acc = op(these.head, acc)
these = these.tail
}
acc
}
// Copy/Paste overrides to avoid interface calls inside loops.
override final def length: Int = {
var these = this
var len = 0
while (!these.isEmpty) {
len += 1
these = these.tail
}
len
}
override final def lengthCompare(len: Int): Int = {
@tailrec def loop(i: Int, xs: List[A]): Int = {
if (i == len)
if (xs.isEmpty) 0 else 1
else if (xs.isEmpty)
-1
else
loop(i + 1, xs.tail)
}
if (len < 0) 1
else loop(0, coll)
}
override final def forall(p: A => Boolean): Boolean = {
var these: List[A] = this
while (!these.isEmpty) {
if (!p(these.head)) return false
these = these.tail
}
true
}
override final def exists(p: A => Boolean): Boolean = {
var these: List[A] = this
while (!these.isEmpty) {
if (p(these.head)) return true
these = these.tail
}
false
}
override final def contains[A1 >: A](elem: A1): Boolean = {
var these: List[A] = this
while (!these.isEmpty) {
if (these.head == elem) return true
these = these.tail
}
false
}
override final def find(p: A => Boolean): Option[A] = {
var these: List[A] = this
while (!these.isEmpty) {
if (p(these.head)) return Some(these.head)
these = these.tail
}
None
}
override def last: A = {
if (isEmpty) throw new NoSuchElementException("List.last")
else {
var these = this
var scout = tail
while (!scout.isEmpty) {
these = scout
scout = scout.tail
}
these.head
}
}
override def corresponds[B](that: collection.Seq[B])(p: (A, B) => Boolean): Boolean = that match {
case that: LinearSeq[B] =>
var i = this
var j = that
while (!(i.isEmpty || j.isEmpty)) {
if (!p(i.head, j.head))
return false
i = i.tail
j = j.tail
}
i.isEmpty && j.isEmpty
case _ =>
super.corresponds(that)(p)
}
override protected[this] def className = "List"
/** Builds a new list by applying a function to all elements of this list.
* Like `xs map f`, but returns `xs` unchanged if function
* `f` maps all elements to themselves (as determined by `eq`).
*
* @param f the function to apply to each element.
* @tparam B the element type of the returned collection.
* @return a list resulting from applying the given function
* `f` to each element of this list and collecting the results.
*/
@`inline` final def mapConserve[B >: A <: AnyRef](f: A => B): List[B] = {
// Note to developers: there exists a duplication between this function and `reflect.internal.util.Collections#map2Conserve`.
// If any successful optimization attempts or other changes are made, please rehash them there too.
@tailrec
def loop(mappedHead: List[B], mappedLast: ::[B], unchanged: List[A], pending: List[A]): List[B] = {
if (pending.isEmpty) {
if (mappedHead eq null) unchanged
else {
mappedLast.next = (unchanged: List[B])
mappedHead
}
}
else {
val head0 = pending.head
val head1 = f(head0)
if (head1 eq head0.asInstanceOf[AnyRef])
loop(mappedHead, mappedLast, unchanged, pending.tail)
else {
var xc = unchanged
var mappedHead1: List[B] = mappedHead
var mappedLast1: ::[B] = mappedLast
while (xc ne pending) {
val next = new ::[B](xc.head, Nil)
if (mappedHead1 eq null) mappedHead1 = next
if (mappedLast1 ne null) mappedLast1.next = next
mappedLast1 = next
xc = xc.tail
}
val next = new ::(head1, Nil)
if (mappedHead1 eq null) mappedHead1 = next
if (mappedLast1 ne null) mappedLast1.next = next
mappedLast1 = next
val tail0 = pending.tail
loop(mappedHead1, mappedLast1, tail0, tail0)
}
}
}
val result = loop(null, null, this, this)
releaseFence()
result
}
override def filter(p: A => Boolean): List[A] = filterCommon(p, isFlipped = false)
override def filterNot(p: A => Boolean): List[A] = filterCommon(p, isFlipped = true)
private[this] def filterCommon(p: A => Boolean, isFlipped: Boolean): List[A] = {
// everything seen so far so far is not included
@tailrec def noneIn(l: List[A]): List[A] = {
if (l.isEmpty)
Nil
else {
val h = l.head
val t = l.tail
if (p(h) != isFlipped)
allIn(l, t)
else
noneIn(t)
}
}
// everything from 'start' is included, if everything from this point is in we can return the origin
// start otherwise if we discover an element that is out we must create a new partial list.
@tailrec def allIn(start: List[A], remaining: List[A]): List[A] = {
if (remaining.isEmpty)
start
else {
val x = remaining.head
if (p(x) != isFlipped)
allIn(start, remaining.tail)
else
partialFill(start, remaining)
}
}
// we have seen elements that should be included then one that should be excluded, start building
def partialFill(origStart: List[A], firstMiss: List[A]): List[A] = {
val newHead = new ::(origStart.head, Nil)
var toProcess = origStart.tail
var currentLast = newHead
// we know that all elements are :: until at least firstMiss.tail
while (!(toProcess eq firstMiss)) {
val newElem = new ::(toProcess.head, Nil)
currentLast.next = newElem
currentLast = newElem
toProcess = toProcess.tail
}
// at this point newHead points to a list which is a duplicate of all the 'in' elements up to the first miss.
// currentLast is the last element in that list.
// now we are going to try and share as much of the tail as we can, only moving elements across when we have to.
var next = firstMiss.tail
var nextToCopy = next // the next element we would need to copy to our list if we cant share.
while (!next.isEmpty) {
// generally recommended is next.isNonEmpty but this incurs an extra method call.
val head: A = next.head
if (p(head) != isFlipped) {
next = next.tail
} else {
// its not a match - do we have outstanding elements?
while (!(nextToCopy eq next)) {
val newElem = new ::(nextToCopy.head, Nil)
currentLast.next = newElem
currentLast = newElem
nextToCopy = nextToCopy.tail
}
nextToCopy = next.tail
next = next.tail
}
}
// we have remaining elements - they are unchanged attach them to the end
if (!nextToCopy.isEmpty)
currentLast.next = nextToCopy
newHead
}
val result = noneIn(this)
releaseFence()
result
}
override def partition(p: A => Boolean): (List[A], List[A]) = {
if (isEmpty) List.TupleOfNil
else super.partition(p) match {
case (Nil, xs) => (Nil, this)
case (xs, Nil) => (this, Nil)
case pair => pair
}
}
final override def toList: List[A] = this
// Override for performance
override def equals(o: scala.Any): Boolean = {
@tailrec def listEq(a: List[_], b: List[_]): Boolean =
(a eq b) || {
val aEmpty = a.isEmpty
val bEmpty = b.isEmpty
if (!(aEmpty || bEmpty) && a.head == b.head) {
listEq(a.tail, b.tail)
}
else {
aEmpty && bEmpty
}
}
o match {
case that: List[_] => listEq(this, that)
case _ => super.equals(o)
}
}
// TODO: uncomment once bincompat allows (reference: scala/scala#9365)
/*
// Override for performance: traverse only as much as needed
// and share tail when nothing needs to be filtered out anymore
override def diff[B >: A](that: collection.Seq[B]): AnyRef = {
if (that.isEmpty || this.isEmpty) this
else if (tail.isEmpty) if (that.contains(head)) Nil else this
else {
val occ = occCounts(that)
val b = new ListBuffer[A]()
@tailrec
def rec(remainder: List[A]): List[A] = {
if(occ.isEmpty) b.prependToList(remainder)
else remainder match {
case Nil => b.result()
case head :: next => {
occ.updateWith(head){
case None => {
b.append(head)
None
}
case Some(1) => None
case Some(n) => Some(n - 1)
}
rec(next)
}
}
}
rec(this)
}
}
*/
}
// Internal code that mutates `next` _must_ call `Statics.releaseFence()` if either immediately, or
// before a newly-allocated, thread-local :: instance is aliased (e.g. in ListBuffer.toList)
final case class :: [+A](override val head: A, private[scala] var next: List[A @uncheckedVariance]) // sound because `next` is used only locally
extends List[A] {
releaseFence()
override def headOption: Some[A] = Some(head)
override def tail: List[A] = next
}
case object Nil extends List[Nothing] {
override def head: Nothing = throw new NoSuchElementException("head of empty list")
override def headOption: None.type = None
override def tail: Nothing = throw new UnsupportedOperationException("tail of empty list")
override def last: Nothing = throw new NoSuchElementException("last of empty list")
override def init: Nothing = throw new UnsupportedOperationException("init of empty list")
override def knownSize: Int = 0
override def iterator: Iterator[Nothing] = Iterator.empty
override def unzip[A1, A2](implicit asPair: Nothing => (A1, A2)): (List[A1], List[A2]) = EmptyUnzip
@transient
private[this] val EmptyUnzip = (Nil, Nil)
}
/**
* $factoryInfo
* @define coll list
* @define Coll `List`
*/
@SerialVersionUID(3L)
object List extends StrictOptimizedSeqFactory[List] {
private val TupleOfNil = (Nil, Nil)
def from[B](coll: collection.IterableOnce[B]): List[B] = Nil.prependedAll(coll)
def newBuilder[A]: Builder[A, List[A]] = new ListBuffer()
def empty[A]: List[A] = Nil
@transient
private[collection] val partialNotApplied = new Function1[Any, Any] { def apply(x: Any): Any = this }
}