Typ scala.collection.immutable.List (w skrócie List
) występuje jako dwa byty - klasa i obiekt (towarzyszący).
Przyjrzyjmy się klasie List.
Zacznijmy od scaladoc dla tej klasy, w której napisano:
“Klasa List reprezentuje niezmienniczą listę jednokierunkową z elementami wybranego typu i pewnym porządku”
Klasa posiada dwie implementacje - obiekt scala.Nil
oraz klasę scala.::
, które udostępniają metody isEmpty
, head
oraz tail
.
Klasa List
jest dopasowana do problemów, w których potrzeba zastosować “liniową strukturę danych, w której dane dokładane są na wierzch stosu i z wierzchołka stosu są pobierane”).
Koszt dodania elementu na początku listy oraz dostępu do elementu początkowego i listy pozostałych jest stały O(1)
. Natomiast, koszt większości pozostałych operacji - length
, append
, reverse
- wynosi O(n)
dla n
-elementowej listy.
Ciekawą lekturą odnośnie efektywności działania operacji różnych strukturach danych, w tym List
jest dokument Collections - Performance Characteristics z oficjalnej dokumentacji języka Scala.
Warto pamiętać, że jakkolwiek List
współdzieli swoją strukturę, co wpływa na jej wydajność pod kątem szybkości działania oraz oszczędności pamięci, ta efektywność tracona jest w procesie serializacji i deserializacji - każda referencja otrzyma własną kopię listy.
List
, Nil
oraz ::
są aliasowane w pakiecie scala
, co sprawia, że ich użycie nie wymaga importowania (gdyż pakiet scala
jest importowany przez kompilator).
Dla przypomnienia: Najefektywniejszymi operacjami na listach jest dostęp do pierwszego elementu (ang. head) oraz ogona (ang. tail), czyli dodanie lub usunięcie elementu jest najefektywniejsze na początku listy.
scala> List(1,2,3) ++ List(4,5,6)
res7: List[Int] = List(1, 2, 3, 4, 5, 6)
scala> 0 :: List(1,2,3)
res12: List[Int] = List(0, 1, 2, 3)
Intensywnie używana w dopasowywaniu wzorców (ang. pattern matching) i raczej o “historycznym” znaczeniu, gdyż istniała w innych językach. Korzystać z umiarem na rzecz +:
.
scala> 0 +: List(1,2,3)
res29: List[Int] = List(0, 1, 2, 3)
scala> List(1,2,3)(0)
res13: Int = 1
scala> List(1,2,3).contains(2)
res19: Boolean = true
scala> List(1,2,3).count( _ > 0)
res21: Int = 3
scala> List(1,2,3).drop(2)
res22: List[Int] = List(3)
scala> List(1,2,3).dropWhile(_ < 2)
res23: List[Int] = List(2, 3)
scala> List(1,2,3).exists(_ == 2)
res24: Boolean = true
scala> List(1,2,3).filter(_ % 2 == 1)
res26: List[Int] = List(1, 3)
scala> List(1,2,3).filterNot(_ % 2 == 1)
res27: List[Int] = List(2)
scala> List(1,2,3).find(_ > 1)
res28: Option[Int] = Some(2)
scala> List(1,2,3).find(_ == 3)
res29: Option[Int] = Some(3)
scala> List(1,2,3).find(_ > 4)
res30: Option[Int] = None
scala> def f(x: Int) = List(x, x*x)
f: (x: Int)List[Int]
scala> List(1,2,3).flatMap(x => f(x))
res31: List[Int] = List(1, 1, 2, 4, 3, 9)
scala> List(1,5,10).fold(0)(_ + _)
res32: Int = 16
scala> List(1,5,10).foldLeft(0)(_ + _)
res33: Int = 16
scala> List(1,5,10).foldRight(0)(_ + _)
res34: Int = 16
scala> List(1,2,3,4,5).forall(_ > 0)
res35: Boolean = true
scala> List(1,2,3,4,5).forall(_ <= 5)
res36: Boolean = true
scala> List(1,2,3,4,5).forall(_ == 3)
res37: Boolean = false
scala> List(1,2,3,4,5).foreach(println)
1
2
3
4
5
scala> List("Scala", "Java", "Groovy", "Python", "PHP", "Pascal", "C++", "C#", "C").groupBy(_.charAt(0))
res38: scala.collection.immutable.Map[Char,List[java.lang.String]] = Map(J -> List(Java), G -> List(Groovy), P -> List(Python, PHP, Pascal), C -> List(C++, C#, C), S -> List(Scala))
scala> List(1,2,3,4,5,6).grouped(2).toList
res39: List[List[Int]] = List(List(1, 2), List(3, 4), List(5, 6))
scala> List(2,4,8,16).head
res40: Int = 2
scala> List("Scala", "Java", "Groovy", "Python").indexOf("Java")
res41: Int = 1
scala> List(1,2,3,4,5,6).indexWhere(_ % 3 == 0)
res42: Int = 2
scala> List(1,2,3,4,5,6).init
res43: List[Int] = List(1, 2, 3, 4, 5)
scala> List().isEmpty
res44: Boolean = true
scala> List(1).isEmpty
res45: Boolean = false
scala> List().iterator
res46: Iterator[Nothing] = empty iterator
scala> List(1,2,3,4).iterator
res47: Iterator[Int] = non-empty iterator
scala> List(1,2,3).length
res48: Int = 3
scala> def f(x: Int) = x*x
f: (x: Int)Int
scala> List(1,2,3).map(f(_))
res49: List[Int] = List(1, 4, 9)
scala> List(1,2,3).map(x => f(x))
res50: List[Int] = List(1, 4, 9)
scala> List(1,4,6,2,3,0).max
res51: Int = 6
scala> List("Scala", "Java", "Groovy").maxBy(_.length)
res52: java.lang.String = Groovy
scala> List(5,3,6,7,2,4).min
res53: Int = 2
scala> List("Scala", "Java", "Groovy").minBy(_.length)
res54: java.lang.String = Java
scala> List(1,2,3,4).mkString
res55: String = 1234
scala> List(1,2,3,4).mkString(",")
res56: String = 1,2,3,4
scala> List(1,2,3,4).mkString("{", ",", "}")
res57: String = {1,2,3,4}
scala> List(1,2,3,4,5,6).par
res58: scala.collection.parallel.immutable.ParSeq[Int] = ParVector(1, 2, 3, 4, 5, 6)
scala> List(1,2,3,4,5,6,7).partition(_ % 2 == 0)
res59: (List[Int], List[Int]) = (List(2, 4, 6),List(1, 3, 5, 7))
scala> List(1,2,3).permutations.toList
res60: List[List[Int]] = List(List(1, 2, 3), List(1, 3, 2), List(2, 1, 3), List(2, 3, 1), List(3, 1, 2), List(3, 2, 1))
scala> List(1,3,2,5).prefixLength(_ < 5)
res61: Int = 3
scala> List(1,2,3,4,5).reduce(_ * _)
res62: Int = 120
scala> List(1,2,3,4,5).reduceLeft(_ * _)
res63: Int = 120
scala> List(1,2,3,4,5).reduceRight(_ * _)
res64: Int = 120
scala> List(1,2,3,4,5).reverse
res65: List[Int] = List(5, 4, 3, 2, 1)
scala> List("Scala", "Java", "Groovy", "Python", "Ruby", "C").sortBy(_.charAt(0))
res66: List[java.lang.String] = List(C, Groovy, Java, Python, Ruby, Scala)
scala> List(1,2,3,4,5,6).span(_ < 4)
res67: (List[Int], List[Int]) = (List(1, 2, 3),List(4, 5, 6))
scala> List(1,2,3,4,5,6).tail
res68: List[Int] = List(2, 3, 4, 5, 6)
scala> List("a", "b", "c", "d", "e", "f", "g").take(3)
res69: List[java.lang.String] = List(a, b, c)
scala> List("c", "b", "a", "d", "f", "e").takeWhile(_ != "f")
res70: List[java.lang.String] = List(c, b, a, d)
scala> List("c", "b", "a", "d", "f", "e").toSeq
res71: scala.collection.immutable.Seq[java.lang.String] = List(c, b, a, d, f, e)
// lazy
scala> def view = List(1,2,3).view.map{x => println(x+"*"+x+"="+x*x); x*x}
view: scala.collection.SeqView[Int,Seq[_]]
scala> view(2)
3*3=9
res72: Int = 9
scala> view(1)
2*2=4
res73: Int = 4
// eager
scala> List(1,2,3).map{x => println(x+"*"+x+"="+x*x); x*x}
1*1=1
2*2=4
3*3=9
res74: List[Int] = List(1, 4, 9)
scala> List(1,2,3,4).withFilter(_ % 2 == 0).map(x => x * x)
res75: List[Int] = List(4, 16)
scala> List("a","b","c").zip(List(1,2,3))
res76: List[(java.lang.String, Int)] = List((a,1), (b,2), (c,3))
scala> List("a","b","c").zipWithIndex
res77: List[(java.lang.String, Int)] = List((a,0), (b,1), (c,2))