Scala Language
Kolekcje
Szukaj…
Sortuj listę
Zakładając poniższą listę , możemy posortować różne sposoby.
val names = List("Kathryn", "Allie", "Beth", "Serin", "Alana")
Domyślne zachowanie sorted()
polega na użyciu math.Ordering
, które dla łańcuchów powoduje sortowanie leksykalne :
names.sorted
// results in: List(Alana, Allie, Beth, Kathryn, Serin)
sortWith
pozwala na zapewnienie własnego zamówienia z wykorzystaniem funkcji porównania:
names.sortWith(_.length < _.length)
// results in: List(Beth, Allie, Serin, Alana, Kathryn)
sortBy
pozwala na zapewnienie funkcji transformacji:
//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)
Zawsze możesz odwrócić listę lub listę posortowaną, używając `reverse:
names.sorted.reverse
//results in: List(Serin, Kathryn, Beth, Allie, Alana)
Listy można również sortować przy użyciu metody Java java.util.Arrays.sort
i jej opakowania Scala scala.util.Sorting.quickSort
java.util.Arrays.sort(data)
scala.util.Sorting.quickSort(data)
Te metody mogą poprawić wydajność podczas sortowania większych kolekcji, jeśli można uniknąć konwersji kolekcji i rozpakowywania / boksu. Aby uzyskać bardziej szczegółową dyskusję na temat różnic w wydajności, przeczytaj o sortowaniu Scala Collection, sortWith i sortBy Performance .
Utwórz listę zawierającą n kopii x
Aby utworzyć kolekcję n
kopii jakiegoś obiektu x
, użyj metody fill . W tym przykładzie tworzona jest List
, ale może ona działać z innymi kolekcjami, dla których fill
ma sens:
// List.fill(n)(x)
scala > List.fill(3)("Hello World")
res0: List[String] = List(Hello World, Hello World, Hello World)
Lista i wektor Cheatheet
Najlepszą praktyką jest teraz stosowanie
Vector
zamiastList
ponieważ implementacje mają lepszą wydajność. Charakterystykę wydajności można znaleźć tutaj .Vector
można stosować wszędzie tam, gdzie używana jestList
.
Tworzenie listy
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
Weź 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
Przygotuj elementy
0 :: List(1, 2, 3) // List(0, 1, 2, 3)
Dołącz elementy
List(1, 2, 3) :+ 4 // List(1, 2, 3, 4), complexity is O(n)
Dołącz (konkatenuj) listy
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)
Wspólne operacje
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"
Arkusz kolekcji map
Zauważ, że dotyczy to tworzenia kolekcji typu
Map
, która różni się od metodymap
.
Tworzenie mapy
Map[String, Int]()
val m1: Map[String, Int] = Map()
val m2: String Map Int = Map()
Mapę można uznać za zbiór tuples
dla większości operacji, gdzie pierwszy element jest kluczem, a drugi wartością.
val l = List(("a", 1), ("b", 2), ("c", 3))
val m = l.toMap // Map(a -> 1, b -> 2, c -> 3)
Zdobądź element
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)
Dodaj element (y)
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)
Wspólne operacje
W operacjach, w których występuje iteracja nad mapą ( map
, find
, forEach
itp.), Elementami kolekcji są tuples
. W parametrze funkcji można użyć krotek ( _1
, _2
) lub funkcji częściowej z blokiem obserwacji:
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
Mapuj i filtruj kolekcję
Mapa
„Mapowanie” w kolekcji wykorzystuje funkcję map
do transformacji każdego elementu tej kolekcji w podobny sposób. Ogólna składnia to:
val someFunction: (A) => (B) = ???
collection.map(someFunction)
Możesz zapewnić anonimową funkcję:
collection.map((x: T) => /*Do something with x*/)
Mnożenie liczb całkowitych przez dwa
// 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)
Filtr
filter
jest używany, gdy chcesz wykluczyć lub „odfiltrować” niektóre elementy kolekcji. Podobnie jak w przypadku map
, ogólna składnia przyjmuje funkcję, ale ta funkcja musi zwrócić wartość Boolean
:
val someFunction: (a) => Boolean = ???
collection.filter(someFunction)
Możesz zapewnić anonimową funkcję bezpośrednio:
collection.filter((x: T) => /*Do something that returns a Boolean*/)
Sprawdzanie numerów par
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)
Więcej przykładów map i filtrów
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 )
Wprowadzenie do kolekcji Scala
Ramy kolekcji Scala, według jej autorów , zostały zaprojektowane tak, aby były łatwe w użyciu, zwięzłe, bezpieczne, szybkie i uniwersalne.
Struktura składa się z cech Scali , które zostały zaprojektowane jako elementy składowe do tworzenia kolekcji. Aby uzyskać więcej informacji na temat tych elementów, przeczytaj oficjalny przegląd kolekcji Scala .
Te wbudowane kolekcje są podzielone na niezmienne i zmienne pakiety. Domyślnie używane są niezmienne wersje. Konstruowanie List()
(bez importowania czegokolwiek) spowoduje utworzenie niezmiennej listy.
Jedną z najpotężniejszych funkcji frameworka jest spójny i łatwy w użyciu interfejs w kolekcjach o podobnych poglądach. Na przykład sumowanie wszystkich elementów w kolekcji jest takie samo dla list, zestawów, wektorów, sekwencji i tablic:
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
Te podobnie myślące typy dziedziczą od cechy Traversable
.
Najlepszą praktyką jest teraz stosowanie
Vector
zamiastList
ponieważ implementacje mają lepszą wydajność. Charakterystykę wydajności można znaleźć tutaj .Vector
można stosować wszędzie tam, gdzie używana jestList
.
Typy przejezdne
Klasy kolekcji posiadające cechę Traversable
implementują foreach
i dziedziczą wiele metod wykonywania wspólnych operacji na kolekcjach, które wszystkie działają identycznie. Najczęstsze operacje są wymienione tutaj:
- Map -
map
,flatMap
icollect
produkować nowe kolekcje, stosując funkcję do każdego elementu w oryginalnej kolekcji.
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")
- Konwersje -
toList
,toArray
i wiele innych operacji konwersji zmieniają bieżącą kolekcję w bardziej konkretny rodzaj kolekcji. Są to zwykle metody poprzedzone znakiem „do”, a bardziej szczegółowy typ (tzn. „ToList” konwertuje naList
).
val array: Array[Int] = List[Int](1, 2, 3).toArray // convert list of ints to array of ints
- Informacje o rozmiarze -
isEmpty
,nonEmpty
,size
ihasDefiniteSize
to metadane dotyczące zestawu. Umożliwia to warunkowe operacje na kolekcji lub kod określający rozmiar kolekcji, w tym jej nieskończoność lub dyskretność.
List().isEmpty // true
List(1).nonEmpty // true
- Pobieranie elementu -
head
,last
,find
i ich wariantyOption
służą do pobierania pierwszego lub ostatniego elementu lub znajdowania określonego elementu w kolekcji.
val list = List(1, 2, 3)
list.head // = 1
list.last // = 3
- Operacje pobierania podkolekcji - operacje
filter
,tail
,slice
,drop
i inne pozwalają na wybranie części kolekcji do dalszej pracy.
List(-2, -1, 0, 1, 2).filter(num => num > 0) // = List(1, 2)
- Operacje
partition
-partition
,partition
splitAt
,span
igroupBy
podział bieżącej kolekcji na różne części.
// split numbers into < 0 and >= 0
List(-2, -1, 0, 1, 2).partition(num => num < 0) // = (List(-2, -1), List(0, 1, 2))
- Testy elementów -
exists
,forall
icount
są operacjami używanymi do sprawdzenia tej kolekcji, aby sprawdzić, czy spełnia predykat.
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
ireduceRight
są używane do zastosowania funkcji binarnych do kolejnych elementów w kolekcji. Idź tutaj, aby zobaczyć przykłady foldów i przejdź tutaj, aby zmniejszyć przykłady .
Zagięcie
Metoda fold
iteruje kolekcję, wykorzystując początkową wartość akumulatora i stosując funkcję, która używa każdego elementu do pomyślnej aktualizacji akumulatora:
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
W powyższym przykładzie dostarczono anonimową funkcję fold()
. Możesz także użyć nazwanej funkcji, która pobiera dwa argumenty. Biorąc to pod uwagę, powyższy przykład można przepisać w następujący sposób:
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
Zmiana wartości początkowej wpłynie na wynik:
initialValue = 2;
sum = nums.fold(initialValue){
(accumulator,currentElementBeingIterated) => accumulator + currentElementBeingIterated
}
println(sum) //prints 17 because 2+1+2+3+4+5 = 17
Metoda fold
ma dwa warianty - foldLeft
i foldRight
.
foldLeft()
od lewej do prawej (od pierwszego elementu kolekcji do ostatniego w tej kolejności). foldRight()
iteracji od prawej do lewej (od ostatniego elementu do pierwszego elementu). fold()
iteruje od lewej do prawej jak foldLeft()
. W rzeczywistości fold()
faktycznie wywołuje foldLeft()
wewnętrznie.
def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op)
fold()
, foldLeft()
i foldRight()
zwracają wartość tego samego typu z wartością początkową, którą przyjmuje. Jednak, w przeciwieństwie do foldLeft()
i foldRight()
, wartość początkowa podana dla fold()
może być tylko tego samego typu lub nadtypu typu kolekcji.
W tym przykładzie kolejność nie jest istotna, więc możesz zmienić fold()
na foldLeft()
lub foldRight()
a wynik pozostanie taki sam. Użycie funkcji wrażliwej na porządek zmieni wyniki.
W razie wątpliwości wybierz foldLeft()
foldRight()
. foldRight()
jest mniej wydajny.
Dla każdego
foreach
jest niezwykły wśród iteratorów kolekcji, ponieważ nie zwraca wyniku. Zamiast tego stosuje funkcję do każdego elementu, który ma tylko skutki uboczne. Na przykład:
scala> val x = List(1,2,3)
x: List[Int] = List(1, 2, 3)
scala> x.foreach { println }
1
2
3
Funkcja dostarczona do foreach
może mieć dowolny typ zwracany, ale wynik zostanie odrzucony . Zazwyczaj foreach
stosuje się, gdy pożądane są działania niepożądane. Jeśli chcesz przekształcić dane, rozważ użycie map
, filter
, for comprehension
lub innej opcji.
Przykład odrzucania wyników
def myFunc(a: Int) : Int = a * 2
List(1,2,3).foreach(myFunc) // Returns nothing
Redukować
Metody reduce()
, reduce()
reduceLeft()
i reduceRight
są podobne do reduceRight
. Funkcja przekazana do zmniejszenia przyjmuje dwie wartości i daje trzecią. Podczas pracy na liście pierwsze dwie wartości są pierwszymi dwiema wartościami na liście. Wynik funkcji i następna wartość na liście są następnie ponownie stosowane do funkcji, co daje nowy wynik. Ten nowy wynik jest stosowany z następną wartością listy i tak dalej, aż nie będzie już żadnych elementów. Ostateczny wynik jest zwracany.
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
Istnieją pewne różnice w działaniu funkcji zmniejszania w porównaniu do funkcji składania. Oni są:
- Funkcje redukcji nie mają początkowej wartości akumulatora.
- Funkcji ograniczania nie można wywoływać na pustych listach.
- Funkcje zmniejszania mogą zwracać tylko typ lub nadtyp listy.