Scala Language
Sammlungen
Suche…
Liste sortieren
Angenommen, die folgende Liste kann auf verschiedene Arten sortiert werden.
val names = List("Kathryn", "Allie", "Beth", "Serin", "Alana")
Das Standardverhalten von sorted()
ist die Verwendung von math.Ordering
, was für Strings zu einer lexographischen Sortierung führt:
names.sorted
// results in: List(Alana, Allie, Beth, Kathryn, Serin)
sortWith
können Sie Ihre eigene Bestellung über eine Vergleichsfunktion sortWith
:
names.sortWith(_.length < _.length)
// results in: List(Beth, Allie, Serin, Alana, Kathryn)
sortBy
können Sie eine Transformationsfunktion bereitstellen:
//A set of vowels to use
val vowels = Set('a', 'e', 'i', 'o', 'u')
//A function that counts the vowels in a name
def countVowels(name: String) = name.count(l => vowels.contains(l.toLower))
//Sorts by the number of vowels
names.sortBy(countVowels)
//result is: List(Kathryn, Beth, Serin, Allie, Alana)
Sie können eine Liste oder eine sortierte Liste jederzeit umkehren, indem Sie Folgendes verwenden:
names.sorted.reverse
//results in: List(Serin, Kathryn, Beth, Allie, Alana)
Listen können auch mit der Java-Methode java.util.Arrays.sort
und ihrem Scala-Wrapper scala.util.Sorting.quickSort
sortiert werden
java.util.Arrays.sort(data)
scala.util.Sorting.quickSort(data)
Diese Methoden können die Leistung beim Sortieren größerer Sammlungen verbessern, wenn die Konvertierung der Sammlung und das Unboxing / Boxing vermieden werden können. Weitere Informationen zu den Leistungsunterschieden finden Sie in der Scala Collection, sortiert, sortWith und sortBy Performance .
Erstellen Sie eine Liste mit n Kopien von x
Verwenden Sie die Füllmethode, um eine Sammlung von n
Kopien eines Objekts x
zu erstellen. In diesem Beispiel wird eine List
. Dies kann jedoch mit anderen Sammlungen funktionieren, für die fill
sinnvoll ist:
// List.fill(n)(x)
scala > List.fill(3)("Hello World")
res0: List[String] = List(Hello World, Hello World, Hello World)
Liste und Vektor-Spickzettel
Es ist jetzt eine bewährte Methode,
Vector
anstelle vonList
da die Implementierungen eine bessere Leistung bieten. Performance-Eigenschaften finden Sie hier .Vector
kann überall verwendet werden, woList
verwendet wird.
Listenerstellung
List[Int]() // Declares an empty list of type Int
List.empty[Int] // Uses `empty` method to declare empty list of type Int
Nil // A list of type Nothing that explicitly has nothing in it
List(1, 2, 3) // Declare a list with some elements
1 :: 2 :: 3 :: Nil // Chaining element prepending to an empty list, in a LISP-style
Nimm das Element
List(1, 2, 3).headOption // Some(1)
List(1, 2, 3).head // 1
List(1, 2, 3).lastOption // Some(3)
List(1, 2, 3).last // 3, complexity is O(n)
List(1, 2, 3)(1) // 2, complexity is O(n)
List(1, 2, 3)(3) // java.lang.IndexOutOfBoundsException: 4
Elemente voranstellen
0 :: List(1, 2, 3) // List(0, 1, 2, 3)
Elemente anhängen
List(1, 2, 3) :+ 4 // List(1, 2, 3, 4), complexity is O(n)
Listen verbinden (verketten)
List(1, 2) ::: List(3, 4) // List(1, 2, 3, 4)
List.concat(List(1,2), List(3, 4)) // List(1, 2, 3, 4)
List(1, 2) ++ List(3, 4) // List(1, 2, 3, 4)
Gemeinsame Operationen
List(1, 2, 3).find(_ == 3) // Some(3)
List(1, 2, 3).map(_ * 2) // List(2, 4, 6)
List(1, 2, 3).filter(_ % 2 == 1) // List(1, 3)
List(1, 2, 3).fold(0)((acc, i) => acc + i * i) // 1 * 1 + 2 * 2 + 3 * 3 = 14
List(1, 2, 3).foldLeft("Foo")(_ + _.toString) // "Foo123"
List(1, 2, 3).foldRight("Foo")(_ + _.toString) // "123Foo"
Kartensammlung Cheatsheet
Beachten Sie, dass dies die Erstellung einer Sammlung vom Typ
Map
betrifft, die sich von dermap
Methode unterscheidet.
Kartenerstellung
Map[String, Int]()
val m1: Map[String, Int] = Map()
val m2: String Map Int = Map()
Eine Map kann für die meisten Operationen als eine Sammlung von tuples
werden, wobei das erste Element der Schlüssel und das zweite Element der Wert ist.
val l = List(("a", 1), ("b", 2), ("c", 3))
val m = l.toMap // Map(a -> 1, b -> 2, c -> 3)
Element abrufen
val m = Map("a" -> 1, "b" -> 2, "c" -> 3)
m.get("a") // Some(1)
m.get("d") // None
m("a") // 1
m("d") // java.util.NoSuchElementException: key not found: d
m.keys // Set(a, b, c)
m.values // MapLike(1, 2, 3)
Element (e) hinzufügen
Map("a" -> 1, "b" -> 2) + ("c" -> 3) // Map(a -> 1, b -> 2, c -> 3)
Map("a" -> 1, "b" -> 2) + ("a" -> 3) // Map(a -> 3, b -> 2)
Map("a" -> 1, "b" -> 2) ++ Map("b" -> 3, "c" -> 4) // Map(a -> 1, b -> 3, c -> 4)
Gemeinsame Operationen
Bei Operationen, bei denen eine Iteration über eine Karte stattfindet ( map
, find
, forEach
usw.), sind die Elemente der Sammlung tuples
. Der Funktionsparameter kann entweder die Tupel-Accessoren ( _1
, _2
) oder eine Teilfunktion mit einem Case-Block verwenden:
m.find(_._1 == "a") // Some((a,1))
m.map {
case (key, value) => (value, key)
} // Map(1 -> a, 2 -> b, 3 -> c)
m.filter(_._2 == 2) // Map(b -> 2)
m.foldLeft(0){
case (acc, (key, value: Int)) => acc + value
} // 6
Karte und Filter über eine Sammlung
Karte
'Mapping' über eine Sammlung hinweg verwendet die map
, um jedes Element dieser Sammlung auf ähnliche Weise zu transformieren. Die allgemeine Syntax lautet:
val someFunction: (A) => (B) = ???
collection.map(someFunction)
Sie können eine anonyme Funktion bereitstellen:
collection.map((x: T) => /*Do something with x*/)
Multiplikation ganzzahliger Zahlen mit zwei
// Initialize
val list = List(1,2,3)
// list: List[Int] = List(1, 2, 3)
// Apply map
list.map((item: Int) => item*2)
// res0: List[Int] = List(2, 4, 6)
// Or in a more concise way
list.map(_*2)
// res1: List[Int] = List(2, 4, 6)
Filter
filter
wird verwendet, wenn Sie bestimmte Elemente einer Sammlung ausschließen oder ausfiltern möchten. Wie bei map
nimmt die allgemeine Syntax eine Funktion an, aber diese Funktion muss einen Boolean
:
val someFunction: (a) => Boolean = ???
collection.filter(someFunction)
Sie können eine anonyme Funktion direkt bereitstellen:
collection.filter((x: T) => /*Do something that returns a Boolean*/)
Paarnummern prüfen
val list = 1 to 10 toList
// list: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
// Filter out all elements that aren't evenly divisible by 2
list.filter((item: Int) => item % 2==0)
// res0: List[Int] = List(2, 4, 6, 8, 10)
Weitere Karten- und Filterbeispiele
case class Person(firstName: String,
lastName: String,
title: String)
// Make a sequence of people
val people = Seq(
Person("Millie", "Fletcher", "Mrs"),
Person("Jim", "White", "Mr"),
Person("Jenny", "Ball", "Miss") )
// Make labels using map
val labels = people.map( person =>
s"${person.title}. ${person.lastName}"
)
// Filter the elements beginning with J
val beginningWithJ = people.filter(_.firstName.startsWith("J"))
// Extract first names and concatenate to a string
val firstNames = people.map(_.firstName).reduce( (a, b) => a + "," + b )
Einführung in die Scala-Sammlungen
Das Scala Collections-Framework ist laut seinen Autoren einfach zu bedienen, präzise, sicher, schnell und universell.
Das Framework besteht aus Scala- Merkmalen , die als Bausteine für das Erstellen von Sammlungen konzipiert wurden. Weitere Informationen zu diesen Bausteinen finden Sie in der offiziellen Übersicht der Scala-Sammlungen .
Diese integrierten Sammlungen sind in unveränderliche und veränderliche Pakete unterteilt. Standardmäßig werden die unveränderlichen Versionen verwendet. Beim Erstellen einer List()
(ohne etwas zu importieren) wird eine unveränderliche Liste erstellt.
Eine der leistungsfähigsten Funktionen des Frameworks ist die konsistente und benutzerfreundliche Oberfläche für gleichgesinnte Sammlungen. Das Summieren aller Elemente in einer Collection ist beispielsweise für Listen, Sets, Vektoren, Seqs und Arrays gleich:
val numList = List[Int](1, 2, 3, 4, 5)
numList.reduce((n1, n2) => n1 + n2) // 15
val numSet = Set[Int](1, 2, 3, 4, 5)
numSet.reduce((n1, n2) => n1 + n2) // 15
val numArray = Array[Int](1, 2, 3, 4, 5)
numArray.reduce((n1, n2) => n1 + n2) // 15
Diese gleichgesinnten Typen erben von der Traversable
Eigenschaft.
Es ist jetzt eine bewährte Methode,
Vector
anstelle vonList
da die Implementierungen eine bessere Leistung bieten. Performance-Eigenschaften finden Sie hier .Vector
kann überall verwendet werden, woList
verwendet wird.
Verfahrbare Typen
Auflistungsklassen mit der Eigenschaft " Traversable
implementieren foreach
und erben viele Methoden zum Ausführen allgemeiner Operationen für Auflistungen, die alle identisch funktionieren. Die häufigsten Vorgänge sind hier aufgelistet:
- Map -
map
,flatMap
undcollect
erzeugen neue Sammlungen, indem auf jedes Element der ursprünglichen Sammlung eine FunktionflatMap
wird.
List(1, 2, 3).map(num => num * 2) // double every number = List(2, 4, 6)
// split list of letters into individual strings and put them into the same list
List("a b c", "d e").flatMap(letters => letters.split(" ")) // = List("a", "b", "c", "d", "e")
- Konvertierungen -
toList
,toArray
und viele andere Konvertierungsvorgänge ändern die aktuelle Sammlung in eine spezifischere Art der Sammlung. Dies sind normalerweise Methoden, denen 'to' vorangestellt wird, und der spezifischere Typ (dh 'toList' konvertiert in eineList
).
val array: Array[Int] = List[Int](1, 2, 3).toArray // convert list of ints to array of ints
- Größeninfo -
isEmpty
,nonEmpty
,size
undhasDefiniteSize
sind alle Metadaten über das Set. Dies ermöglicht bedingte Operationen für die Sammlung oder für Code, um die Größe der Sammlung zu bestimmen, einschließlich, ob sie unendlich oder diskret ist.
List().isEmpty // true
List(1).nonEmpty // true
- Elementabruf -
head
,last
,find
und ihreOption
werden verwendet, um das erste oder letzte Element abzurufen oder ein bestimmtes Element in der Auflistung zu suchen.
val list = List(1, 2, 3)
list.head // = 1
list.last // = 3
- Abrufvorgänge für Unterauflistungen -
filter
,tail
,slice
,drop
und andere Vorgänge ermöglichen die Auswahl von Teilen der Sammlung, die weiter bearbeitet werden können.
List(-2, -1, 0, 1, 2).filter(num => num > 0) // = List(1, 2)
- Unterteilungsoperationen -
partition
,splitAt
,span
undgroupBy
die aktuelle Sammlung in verschiedene TeilegroupBy
.
// split numbers into < 0 and >= 0
List(-2, -1, 0, 1, 2).partition(num => num < 0) // = (List(-2, -1), List(0, 1, 2))
- Elementtests -
exists
undcount
sind Vorgänge, mit denen diese Sammlungforall
wird, umforall
, ob sie ein Vergleichselement erfüllt.
List(1, 2, 3, 4).forall(num => num > 0) // = true, all numbers are positive
List(-3, -2, -1, 1).forall(num => num < 0) // = false, not all numbers are negative
- Folds -
foldLeft
(/:
foldRight
,foldRight
(:\
),reduceLeft
undreduceRight
werden verwendet, um binäre Funktionen auf aufeinanderfolgende Elemente in der Auflistung anzuwenden. Hier finden Sie Beispiele für Faltblätter und hier für reduzierte Beispiele .
Falten
Die fold
Methode durchläuft eine Sammlung, wobei ein anfänglicher Akkumulatorwert verwendet wird und eine Funktion angewendet wird, die jedes Element zum erfolgreichen Aktualisieren des Akkumulators verwendet:
val nums = List(1,2,3,4,5)
var initialValue:Int = 0;
var sum = nums.fold(initialValue){
(accumulator,currentElementBeingIterated) => accumulator + currentElementBeingIterated
}
println(sum) //prints 15 because 0+1+2+3+4+5 = 15
Im obigen Beispiel wurde eine anonyme Funktion für fold()
bereitgestellt. Sie können auch eine benannte Funktion verwenden, die zwei Argumente akzeptiert. Wenn man dies in mir trägt, kann das obige Beispiel folgendermaßen umgeschrieben werden:
def sum(x: Int, y: Int) = x+ y
val nums = List(1, 2, 3, 4, 5)
var initialValue: Int = 0
val sum = nums.fold(initialValue)(sum)
println(sum) // prints 15 because 0 + 1 + 2 + 3 + 4 + 5 = 15
Das Ändern des Anfangswerts wirkt sich auf das Ergebnis aus:
initialValue = 2;
sum = nums.fold(initialValue){
(accumulator,currentElementBeingIterated) => accumulator + currentElementBeingIterated
}
println(sum) //prints 17 because 2+1+2+3+4+5 = 17
Die fold
Methode hat zwei Varianten - foldLeft
und foldRight
.
foldLeft()
durchläuft von links nach rechts (vom ersten Element der Sammlung bis zum letzten in dieser Reihenfolge). foldRight()
iteriert von rechts nach links (vom letzten bis zum ersten Element). fold()
iteriert von links nach rechts wie foldLeft()
. In der Tat ruft fold()
tatsächlich foldLeft()
.
def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op)
fold()
, foldLeft()
und foldRight()
geben einen Wert zurück, der denselben Typ hat wie der ursprüngliche Wert. Im Gegensatz zu foldLeft()
und foldRight()
kann der für fold()
angegebene Anfangswert jedoch nur vom selben Typ oder einem Supertyp des Typs der Sammlung sein.
In diesem Beispiel ist die Reihenfolge nicht relevant. Sie können fold()
in foldLeft()
oder foldRight()
und das Ergebnis bleibt gleich. Die Verwendung einer auftragsempfindlichen Funktion ändert die Ergebnisse.
Im Zweifelsfall ziehen Sie foldLeft()
vor foldRight()
. foldRight()
ist weniger performant.
Für jeden
foreach
ist unter den Iteratoren der Sammlungen ungewöhnlich, da es kein Ergebnis zurückgibt. Stattdessen wird auf jedes Element eine Funktion angewendet, die nur Nebeneffekte hat. Zum Beispiel:
scala> val x = List(1,2,3)
x: List[Int] = List(1, 2, 3)
scala> x.foreach { println }
1
2
3
Die an foreach
gelieferte Funktion kann einen beliebigen Rückgabetyp haben, das Ergebnis wird jedoch verworfen . In der Regel wird foreach
verwendet, wenn Nebenwirkungen erwünscht sind. Wenn Sie Daten transformieren möchten, sollten Sie map
, filter
, ein for comprehension
oder eine andere Option verwenden.
Beispiel für das Verwerfen von Ergebnissen
def myFunc(a: Int) : Int = a * 2
List(1,2,3).foreach(myFunc) // Returns nothing
Reduzieren
Die Methoden reduceRight
reduce()
, reduceLeft()
und reduceRight
(ähnlich reduceLeft()
ähneln Falten. Die zum Reduzieren übergebene Funktion nimmt zwei Werte und liefert einen dritten Wert. Wenn Sie mit einer Liste arbeiten, sind die ersten beiden Werte die ersten beiden Werte in der Liste. Das Ergebnis der Funktion und der nächste Wert in der Liste werden dann erneut auf die Funktion angewendet und ergeben ein neues Ergebnis. Dieses neue Ergebnis wird mit dem nächsten Wert der Liste usw. angewendet, bis keine Elemente mehr vorhanden sind. Das Endergebnis wird zurückgegeben.
val nums = List(1,2,3,4,5)
sum = nums.reduce({ (a, b) => a + b })
println(sum) //prints 15
val names = List("John","Koby", "Josh", "Matilda", "Zac", "Mary Poppins")
def findLongest(nameA:String, nameB:String):String = {
if (nameA.length > nameB.length) nameA else nameB
}
def findLastAlphabetically(nameA:String, nameB:String):String = {
if (nameA > nameB) nameA else nameB
}
val longestName:String = names.reduce(findLongest(_,_))
println(longestName) //prints Mary Poppins
//You can also omit the arguments if you want
val lastAlphabetically:String = names.reduce(findLastAlphabetically)
println(lastAlphabetically) //prints Zac
Es gibt einige Unterschiede in der Funktionsweise der Reduktionsfunktionen im Vergleich zu den Falzfunktionen. Sie sind:
- Die Reduzierfunktionen haben keinen anfänglichen Speicherwert.
- Reduktionsfunktionen können bei leeren Listen nicht aufgerufen werden.
- Reduktionsfunktionen können nur den Typ oder den Supertyp der Liste zurückgeben.