List

Einstieg
Eine List erzeugen
Elementzugriff
Methoden der Klasse List

Einstieg

Die Klasse scala.collection.immutable.List ist eine der am meist verwendeten Datenstrukturen in Scala. Es handelt sich dabei um eine sequenzielle Liste eines bestimmten Datentyps. Dementsprechend enthält eine List[Double] nur Elemente vom Datentyp Double. Da List eine unveränderliche Datenstruktur ist, können die enthaltenen Elemente nicht verändert werden. Der "Zustand" eines enthaltenen Elementes kann evtl. doch verändert werden, da sich die Unveränderbarkeit nur auf die enthaltenen Referenzen, jedoch nicht auf die in den Referenzen enthaltenen Elemente, bezieht.

Die API-Dokumentation der Klasse List finden wir unter http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.List

Eine List erzeugen

Da es sich bei der Klasse scala.collection.immutable.List um eine abstrakte Klasse handelt, können wir eine List nicht mit dem Schlüsselwort new instanziieren. Eine Möglichkeit ist die Verwendung der Methode apply aus dem Begleitobjekt der Klasse List zu verwenden. Die Methode apply dient hier als Factory-Methode zur Erzeugung von Objekten des Typs List.

scala> val list : List[Int] = List.apply(2,1,3)
list: List[Int] = List(2, 1, 3)
timpt.de - X2H V 0.11

Die Syntax von Scala erlaubt es die Angabe des Methodennamens apply wegzulassen, wenn direkt hinter dem Klassennamen die Runden Klammern angegeben werden. Da auch der Typparameter vom Compiler abgeleitet werden kann, ist die Erzeugung obiger Liste auch mit folgender Syntax möglich:

scala> val list = List(2,1,3)
list: List[Int] = List(2, 1, 3)
timpt.de - X2H V 0.11

Eine weitere Möglichkeit eine List zu erzeugen ist die Methode :: der Klasse List. :: wird auch mit dem Namen "cons" bezeichnet. In der Operatorschreibweise kommt auch "Cons-Operator" als Bezeichnung vor. Da :: eine Methode der Klasse List ist, kann die neue List nur auf Basis einer bestehenden List erzeugt werden. Ist keine List als Basis vorhanden, kann Nil, welches eine leere List repräsentiert, verwendet werden.

In der Operatorschreibweise wird die Ausgangs-List am Ende des gesamten Ausdrucks angegeben. Dem ist so, da der "Cons-Operator" auf einem Doppelpunkt endet und somit die Methode auf dem rechten Operanden ausgeführt wird.

scala> val list = 2 :: 1 :: 3 :: Nil
list: List[Int] = List(2, 1, 3)
timpt.de - X2H V 0.11

Elementzugriff

Nachdem wir nun wissen, wie wir eine List erzeugen können, ist es nun an der Zeit herauszufinden, wie wir wieder Zugriff auf die Elemente erhalten. Auch hier hilft uns wieder die Methode apply, aber diesmal aus der Begleitklasse. Die Methode apply erwartet hier den Index des gewünschten Objektes. Die Indizierung der Elemente erfolgt beginnend mit 0. Um also auf das zweite Element einer List zuzugreifen, verwenden wir als Index 1. Nachfolgend ein REPL Beispiel, wo eine List erzeugt wird und anschließend auf das zweite Element zugegriffen wird.

scala> val myList = List(2,11,33,22)
myList: List[Int] = List(2, 11, 33, 22)

scala> println(myList.apply(1))
11
timpt.de - X2H V 0.11

Auch hier erlaubt uns die Scala Syntax, die Angabe des Methodennamens apply wegzulassen. Wir können direkt nach dem Objektnamen den Index des gewünschten Elements (hier das dritte Element) in runden Klammern angegeben.

scala> println(myList(2))
33
timpt.de - X2H V 0.11

Methoden der Klasse List

Für die Klasse List sind eine Fülle nützlicher Methoden definiert. Viele der beschriebenen Methoden / Funktionen sind nicht ausschließlich für die Klasse oder in der Klasse List definiert. Auch wenn es anders erscheint, wird keine List durch den Aufruf einer Methode verändert. Würde eine List durch eine Methode verändert, würde es sich bei einer List nicht um einen unveränderlichen Datentyp handeln. Nachfolgend sollen ausgewählte Methoden der Klasse List vorgestellt werden.

.
++ Verbindet eine List mit den Elementen einer traversablen Collection.
+: Erzeugt eine neue List bestehend aus einem hinzuzufügendem Element, gefolgt von der Ausgangsliste.
:: Erzeugt eine List bestehnd aus einem neuen Element und einer List.
::: Verbindet zwei List Objekte zu einer neuen List.
contains Überprüft, ob ein Element in der Liste enthalten ist.
drop Erzeugt eine neue List mit den Elementen der Ursprungsliste, ohne die ersten, im Parameter angegebenen, n Elemente.
dropRight Erzeugt eine neue List mit den Elementen der Ursprungsliste, ohne die letzten, im Parameter angegebenen, n Elemente.
exists Überprüft, ob zumindest ein Element der Liste die Anforderung der übergebenen Funktion entspricht.
filter Erzeugt eine Liste bestehend aus Elementen der Ausgangsliste, die eine zu definierende Bedinung erfüllen.
foreach foreach liefert nacheinander die Elemente der Liste, auf die foreach aufgerufen wird.
head Liefert das erste Element der List.
length Liefert die Anzahl Elemente in der List
map Erzeugt eine neue Liste durch Anwendung einer Funktion auf alle Elemente der Ausgangsliste
reduceLeft Iteriert paarweise über die Liste und wertet die übergebene Funktion auf die übergebenen Parameter aus.
reverse: List[A] Liefert eine List mit allen Elementen der Ursprungsliste, wobei die Elemente in umgekehrter Reihenfolge enthalten sind.
size Liefert die Anzahl Elemente in der List (entspricht length).
tail Liefert eine neue List, ohne das erste Element der List auf dem tail aufgerufen wurde.

++


def ++ [B] (that: GenTraversableOnce[B]): List[B] 
def ++ [B >: A, That] (that: GenTraversableOnce[B])(implicit bf: CanBuildFrom[List[A], B, That]): That 
def ++ [B >: A, That] (that: TraversableOnce[B])(implicit bf: CanBuildFrom[List[A], B, That]): That 
          

Mit ++ wird auf Basis einer List eine neue List erzeugt, die aus den Elementen der List besteht und aus den Elementen der als Parameter übergebenen traversablen Collection.

Das nachfolgende Beispiel zeigt die Erzeugung einer List aus einer List und einem Array.

object PlusPlus {
  def main(args: Array[String]) {
    val list = List(1,2,3,4,5)
    val array = Array(6,7,8,9)    
    val list2 = list ++ array    
    println(list2)
  }
}
timpt.de - X2H V 0.11

Die Ausführung des Programms führt zu folgender Ausgabe auf der Systemausgabe:

List(1, 2, 3, 4, 5, 6, 7, 8, 9)          
          

+:

Erzeugt eine neue List bestehend aus einem hinzuzufügendem Element, gefolgt von der Ausgangsliste.


def +: (elem: A): List[A] 
def +: [B >: A, That] (elem: B)(implicit bf: CanBuildFrom[List[A], B, That]): That          
          
object PrependIt {
  def main(args: Array[String]) {
    val list = List(1,2,3,4)
    val list2 = 5 +: list
    
    println(list2)
  }
}
timpt.de - X2H V 0.10

Die Ausführung des Programms führt zu folgender Ausgabe auf der Systemausgabe:

List(5, 1, 2, 3, 4)
          

::


def :: (x: A) : List[A] 
def :: [B >: A] (x: B): List[B] 
          

Die Methode :: erzeugt eine neue List bestehend aus dem in der Methode übergebenem Argumentes gefolgt vom Inhalt der Liste auf der :: aufgerufen wurde.

Nachfolgend ein Skript Beispiel zur Methode :: in Punkt-Schreibweise.

val list1 = List(2,3,4)
val list2 = list1.::(1)
println(list1)
println(list2)
timpt.de - X2H V 0.7

Die Ausführung dieses Skriptes führt zur folgenden Ausgabe auf der Systemausgabe:

List(2, 3, 4)
List(1, 2, 3, 4)          
          

Wie bei der Methode ::: ist bei der Methode :: die Operator-Schreibweise geläufiger. Da der Methodenname mit einem Doppelpunkt endet, wird diese bei der Operator-Schreibweise auf den rechten Operanden mit dem linken Operanden als Argument aufgerufen.

Das nachfolgende Skript entspricht dem obigen in Operator-Schreibweise und führt bei dessen Ausgabe zum gleichen Ergebnis.

val list1 = List(2,3,4)
val list2 = 1 :: list1
println(list1)
println(list2)
timpt.de - X2H V 0.7

Die Methode :: eignet sich auch dazu einzelne Elemente zu einer Liste zu verbinden. Das nachfolgende Skript zeigt exemplarisch diese Vorgehensweise.

val list1 = 1 :: 2 :: 3 :: 4 :: Nil
println(list1)
timpt.de - X2H V 0.7

Die Ausführung dieses Skriptes führt zu folgender Ausgabe:

List(1, 2, 3, 4)          
          

Da die Methode :: in der Klasse List definiert ist, muss als letztes Element ein List Objekt angegeben werden, worauf die Methode :: aufgerufen wird. In unserem Beispiel ist dies Nil, was einer leeren List entspricht.


:::


def ::: (prefix: List[A]) : List[A] 
def ::: [B >: A] (prefix: List[B]) : List[B]           
          

Die Methode ::: verbindet zwei Listen zu einer neuen List. Die Ergebnisliste beginnt dabei mit den Elementen, die als Argument der Methode übergeben wurde, gefolgt von den Elementen der Liste auf der die Methode ::: aufgerufen wurde.

Im nachfolgendem Skript werden zunächst zwei Listen den Variablen list1 und list2 zugewiesen. Anschließend wird eine neue Liste mit dem Inhalt von list1 und list2 erstellt und der Variablen list3 zugewiesen.

val list1 = List(1,2,3)
val list2 = List(4,5,6)

val list3 = list2.:::(list1)

println(list1)
println(list2)
println(list3)
timpt.de - X2H V 0.7

Die Ausführung des Skriptes führt zu folgender Ausgabe:

List(1, 2, 3)
List(4, 5, 6)
List(1, 2, 3, 4, 5, 6)
          

Die Verwendung der Methode ::: in der Schreibweise mit Punkt und Klammern wird man in der Praxis weniger häufig antreffen. Geläufiger ist die Operator - Schreibweise. Da die Methode mit einem Doppelpunkt endet, wird die Methode auf den rechten Operanden ausgeführt. Dies erhöht deutlich die Lesbarkeit, da die Elemente des linken Operanden vor den Elementen des rechten Operanden im Ergebnis erscheinen.

Das nachfolgende Beispiel entspricht dem obigem Beispiel, mit Ausnahme, das hier die Operator-Schreibweise angewendet wird.

val list1 = List(1,2,3)
val list2 = List(4,5,6)

val list3 = list1 ::: list2

println(list1)
println(list2)
println(list3)
timpt.de - X2H V 0.4

contains


def contains (elem: Any): Boolean 
          

Die Funktion contains überprüft ob ein übergebenes Element in der Liste enthalten ist. Nachfolgend eine beispielhafte Anwendung:

object ContainsIt {
  def main(args: Array[String]){
    val myList = List("123","abc","456","def")
    println(myList contains "1234")
    println(myList contains "abc")
  }
}
timpt.de - X2H V 0.11

Die Ausführung des Programms führt zu folgender Ausgabe auf der Systemausgabe:

false
true          
          

exists


def exists (p: (A) => Boolean): Boolean           
          

Die Funktion exists überprüft, ob zumindest ein Element der Liste die Anforderung der übergebenen Funktion entspricht. Im nachfolgenden Beispiel werden zwei List Objekte mit Int Werten erzeugt. Die Variable myFunction ist einer Funktion zugeordnet, die als Parameter einen Int-Wert erwartet und als Ergebnis true liefert, wenn das Argument durch 2 ohne Rest teilbar ist. Diese Funktion übergeben wir der Funktion exists der zuvor erzeugten Listen. Sollte ein Argument der Liste durch 2 ohne Rest teilbar sein, ist das Ergebnis von exists true andernfalls ist das Ergebnis false.

object ListExists {
  def main(args: Array[String]) {
    val list1 = List(1,2,4,5,7)
    val list2 = List(1,3,5,7)
    
    val myFunction = (x: Int) => x % 2 == 0
    
    println(list1.exists(myFunction))
    println(list2.exists(myFunction))
  }
}
timpt.de - X2H V 0.11

Die Ausführung des Programmes führt zu folgender Ausgabe auf der Systemausgabe:

true
false          
          

filter


def filter (p: (A) => Boolean) : List[A]           
          

Erzeugt eine Liste bestehend aus Elementen der Ausgangsliste, die eine zu definierende Bedingung erfüllen. Die zu erfüllende Bedingung wird der Methode filter über eine Funktion mitgeteilt. Zur Demonstration der Methode filter definieren wir eine List mit Elementen des Datentyps Int:

scala> val list1 = List(1,33,26,146,32,55,26,217,24,326)          
list1: List[Int] = List(1, 33, 26, 146, 32, 55, 26, 217, 24, 326)          
          

Nun erzeugen wir eine neue List über die Methode filter mit allem Elementen aus list1 die größer 50 sind. Um dies zu erreichen, definieren wir beim Aufruf der Methode filter ein Funktionsliteral innerhalb der Klammern der Methode.

scala> val list2 = list1.filter((x: Int) => x > 50)
list2: List[Int] = List(146, 55, 217, 326)
          

Da die List nur aus Int Werten besteht, kann der Scala Compiler den Typ der Variablen über Typableitung (type inference) bestimmen. Daher muss der Typ der Variablen x in der Funktion nicht angegeben werden.

scala> val list3 = list1.filter(x => x < 30)
list3: List[Int] = List(1, 26, 26, 24)
          

Da filter immer ein Element der Filter-Funktion übergibt, kann die Schreibweise der Funktion weiter vereinfacht werden und auf den übergebenen Parameter mit dem Unterstrich zugegriffen werden.

scala> val list4 = list1.filter(_ > 100)
list4: List[Int] = List(146, 217, 326)        
          

Definiert ist filter im Trait scala.collection.TraversableLike.


foreach


def foreach (f: (A) => Unit) : Unit 
def foreach [B] (f: (A) => B) : Unit           
          

Die Methode foreach iteriert über alle Elemente der Liste, welche in einer zu übergebenen Funktion verarbeitet werden können. Das nachfolgende Beispiel zeigt die Verwendung von foreach mit einem Funktionsliteral, welches die enthaltenen Elemente auf der Systemausgabe ausgibt.

object MyListTest {
  def main(args: Array[String]) {
    val myList = List(1,6,3,9,4)       
    myList.foreach(i => println(i))
  }  
}
timpt.de - X2H V 0.10

Die Ausführung dieses Programmes führt zu folgender Ausgabe auf der Systemausgabe:

1
6
3
9
4          
          

Definiert ist foreach im Trait scala.collection.IterableLike, welche im Trait scala.collection.LinearSeqOptimized implementiert wird.



def head : A           
          

Die Funktion head liefert das erste Element einer Liste. Der nachfolgende Quelltext zeigt beispielhaft die Verwendung der Funktion head.

object MyListHeadTest {
  def main(args: Array[String]) {
    val myList = List(4,3,2,1)
    val headOfList = myList.head
    println(headOfList)
  }
}
timpt.de - X2H V 0.10

Als Ergebnis des Programmablaufs wird der Int-Wert 4 auf der Systemausgabe ausgegeben. Definiert ist die Funktion head im Trait scala.collection.TraversableLike und wird im Train scalc.collection.IterableLike überschrieben.


map


def map [B] (f: (A) ? B): List[B] 
def map [B, That] (f: (A) ? B)(implicit bf: CanBuildFrom[List[A], B, That]): That           
          

map erzeugt eine neue Liste durch Anwendung einer Funktion auf alle Elemente der Ausgangsliste. Im nachfolgenden Beispiel wird auf jedes Element der Ausgangsliste eine Funktion angewendet, welche jeden Wert mit 2 multipliziert. Das jeweilige Listenelement wird in der Funktion durch den Unterstrich repräsentiert.

object TheMapFunction {
  def main(args: Array[String]) {
    val list1 = List(1,2,3,4)
    val list2 = list1 map (_ * 2)
    println(list1)
    println(list2)
  }
}
timpt.de - X2H V 0.11

Die Ausführung des Programms führt zu folgender Ausgabe auf der Systemausgabe:

List(1, 2, 3, 4)
List(2, 4, 6, 8)
          

reduceLeft


reduceLeft [B >: A] (f: (B, A) => B) : B
          

reduceLeft iteriert paarweise über die Liste von der linken Seite aus und wertet mit diesen die übergebene Funktion aus. Die Iteration beginnt mit den ersten beiden Elementen der Liste. Das Ergebnis der Auswertung und das nächte Element der Liste werden im Anschluss in gleicher Weise ausgewertet. Diese Iteration wird fortgesetzt bis das Ende der Liste erreicht ist. Das Ergebnis von reduceLeft ist das Ergebnis der letzten Auswertung. Nachfolgend beispielhaft die Anwendung von reduceLeft

object ReduceLeft1{
  def main(args: Array[String]) {
    val myList = List(1,4,5,8,5,2,5)
    myList.reduceLeft{(a,b) => 
      if (a > b) {    
        println("true "+a+" "+b)
        a * 2 
      }
      else {
        println("false "+a+" "+b)
        b
      }        
    }
  }
}
timpt.de - X2H V 0.10

Die Ausführung des Programmes führt zu folgender Ausgabe:

false 1 4
false 4 5
false 5 8
true 8 5
true 16 2
true 32 5          
          
object ReduceLeft2 {
  def main(args: Array[String]) {
    val myList = List(1,4,5,8,5,2,5)
    println(myList.reduceLeft((a,b) => test(a,b)))    
  }
  
  def test(v1: Int, v2: Int) = if (v2 > v1) v2 else v1
}
timpt.de - X2H V 0.10

Die Ausführung des Programmes führt zum Ergebnis 8.

Definiert wird reduceLeft im Trait scala.collection.TraversableOnce und wird im Trait scala.collection.LinearSeqOptimized implementiert.


reverse: List:[A]


def reverse : List[A]           
          

Die Methode reverse: List[A] liefert eine List mit allen Elementen der Ursprungsliste, wobei die Elemente in umgekehrter Reihenfolge enthalten sind.

object ListReverse {
  def main(args: Array[String]) {
    val myList = List(1,2,3,4)
    println(myList)
    val reverseList = myList.reverse
    println(reverseList)
  }
}
timpt.de - X2H V 0.10

Die Ausführung des Programms führt zu folgender Ausgabe auf der Systemausgabe:

List(1, 2, 3, 4)
List(4, 3, 2, 1)          
          

size


def size : Int           
          

Die Funktion size der Klasse List liefert die Anzahl Elemente, die in der List enthalten sind. Das nachfolgende Beispiel erzeugt zunächst eine Liste mit 5 Elementen. In der nachfolgenden Systemausgabe wird die Anzahl der Elemente ausgegeben.

object ListSize extends Application{
  val myList = List(1,2,3,4,5)
  println(myList.size)
}
timpt.de - X2H V 0.10

tail


def tail : List[A]           
          

tail liefert eine neue List, ohne das erste Element der List auf dem tail aufgerufen wurde. Das nachfolgende Skript zeigt beispielhaft die Anwendung von tail.

val list1 = List(1,2,3,4,5,6)
val list2 = list1.tail

println(list1)
println(list2)
timpt.de - X2H V 0.10

Der Ablauf des Skriptes führt zu folgender Ausgabe auf der Systemausgabe:

List(1, 2, 3, 4, 5, 6)
List(2, 3, 4, 5, 6)