Scala is a multi paradigm programming language. This tutorial focuses on functional programming
This tutorial is suitable for people who have mastered the Java programming language
Constants and variables
var can be used to declare a variable:
var var_name[:type] = xxx
Among them, the type declaration of the attribute can be omitted Then scala will automatically guess the type of attribute Even if the type can be omitted, scala is different from python
Scala officials do not recommend defining too many variables. It is best not to define any variables (the effects of all variables are completed through functions)
val can be used to declare a constant:
val val_name[:type] = xxx
Constants cannot be modified
Basic type
Scala provides the following basic data types:
type | explain |
---|---|
Byte | Byte type |
Short | Short |
Int | integer |
Long | Long integer |
Char | character |
String | character string |
Float | float |
Double | Double precision |
Boolean | Boolean |
In addition to String, it is located in Java Lang, the other basic types are under the package of scala Scala doesn't have the concept of all real basic types. In fact, they are all encapsulated classes However, these basic types can be assigned directly in scala (new is not required)
scala and Java Lang packages are automatically introduced by scala, so using these classes does not require any declaration
For example:
val flag = true // Equivalent to val flag: Boolean = true
In Scala, String supports three quotation marks "" "xxx" "" this supports native strings. Strings in three quotation marks will be directly treated as native strings without any escape
Scala provides a rich wrapper class RichXxx for basic types And can realize automatic conversion
Operator
In Scala, an operator is a method, and a method is an operator There are no real operators in scala. Operators can actually be called through functions:
// The following two expressions are the same: val num1 = 2 + 3 val num2 = 2.+(3)
Literal 2 exists as an object, and there is a + method in the object
For a method, you can use the operator's method to call:
// The following two expressions are the same: val str1 = "123456".charAt(1) val str2 = "123456" charAt 1
Scala has no real operators. All operators will be converted into method calls
If the method receives more than one parameter, the bracket cannot be omitted. If there is no parameter, the point and bracket can be omitted:
val str1 = "123456".substring(2, 3) val str2 = "123456" substring (2, 3) val str3 = "a".toUpperCase() val str4 = "a" toUpperCase
Methods in Scala have priority It is reflected as the priority of the operator in the expression For a series of method calls, the method with higher priority will be called first
The priority is determined according to the method name and the first character of the method name. The higher the following characters are, the higher the priority is:
- * / %
- + -
- :
- = !
- <>
- &
- ^
- |
- All letters
- All assignment operators
The priority can be changed through parentheses. Methods with the same priority are called from left to right
Operators fall into three categories:
- Infix operator between two operands
- Suffix operator, which follows a unique operand
- Prefix operator. The operator is in front of a unique operand. Scala only provides four kinds: -, +, ~,% The prefix operator will be translated as "unary_x". For example, "- 3" will be translated as "3.unary_ - ()". We cannot define the prefix operator ourselves
The special method ending with ":" character is called by the right operand and passed in the left operand For example, "a::b" will be translated as "(b).::(a)"
Built in control structure
If can be used as judgment, and its usage is basically the same as java However, if in scala has a return value. If will return the value of the last expression after execution, for example:
val num = 10 val res = if (num > 100) { "num Greater than 100" } else if (num < 100 && num > 5) { "num Between 5 and 100" } else { "num Less than 5" } println(res)
The result of res should be "num between 5 and 100" if is actually a function. It does not print in the function, which meets the concept of functional programming
While and do While can be used for loops, just like java The return value of while in scala is Unit, which means there is no return value While requires variables and may have additional effects, which is not in line with the idea of functional programming. Therefore, the official does not recommend using while to write loops, but recursive replacement
For example, use recursion to implement calculations from 1 to 100:
def sum100(x: Int, sum: Int): Int = { if (x <= 100) { sum100(x + 1, sum + x) } else { sum } } val res = sum100(1, 0) println(res)
All loop controls should be written in such a recursive form
The use of for is similar to that of java You can use for to traverse the collection:
val list = List(1, 3, 4, 5, 12) for (x:Int <- list) { println(x) }
The to operator is used to generate a range of arrays and can be directly used for loops in a certain range:
for (x <- 1 to 5) { println(x) }
for can also implement filtering. for example, you can traverse the even number of 0-100 through the following code:
for (x <- 0 to 100; if x % 2 == 0) { println(x) }
Use multiple ';' You can filter multiple conditions:
for (x <- 0 to 100; if x > 20; if x < 80) { println(x) }
Nested loops can be implemented using for
for (i <- 1 to 9; j <- 1 to i) { print(i * j + ",") }
This prints the results of all multiplications within 10
for also supports variable definition between streams, and can define a shared variable in multiple loops Inter stream variables support if assignment. for example, print a complete 99 multiplication table through the following code:
for (i <- 1 to 9; j <- 1 to i; next = if (i == j) "\n" else "\t") { print(i + "*" + j + "=" + i * j + next) }
The return value of for is also Unit But yield allows for to assemble the last return value of each loop into a collection So the above code can be converted to:
val res = for (i <- 1 to 9; j <- 1 to i; next = if (i == j) "\n" else "\t") yield { i + "*" + j + "=" + i * j + next } println(res)
This meets the requirements of functional programming
scala inherits the exception mechanism of java. You can throw an exception through the throw keyword and try catch... Finally, exceptions can be caught
catch is written differently from java, as follows:
try { ... } catch { case e: Exception1 => ... case e: Exception2 => ... ... } finally { ... }
try can have a return value For example:
val res = try { ... "no exception" } catch { case e: Exception => "have an exception" } println(res)
Note that if it is returned in finally, the return value will not affect the return values of try and catch, which is different from java (java will return the return value of finally anyway)
match ... case is similar to java switch case. Here is an example:
val str = "abc" val res = str match { case "abc" => "I am abc" case "bbb" => "I am bbb" case _ => "I am neither abc Neither bbb" } println(res)
There is no need to write break, "_" Represents any data
Continue and break are not present in scala If you want to jump out or continue the loop, you can use recursive implementation
function
Scala supports functional programming, and function is the most important concept in scala
A function is a collection of code with specific functions, which is composed of function modifier, function name, parameter list, return value declaration and function body
[private | protected] [override] [final] def fun_name(param: type[,param: type]...) [:retType] = { ... }
Permission can control the callable domain of a function. The default is public Override and final are concepts used in OOP
The return value type can be omitted and automatically inferred by scala
If the function body has only one line, the curly braces of the function body can be omitted
If you return a value in the function body without using the return keyword, the return of the last statement is used as the return value of the function by default
Unit means no return value. If a function declaration has no return value declaration and there is no "=" in front of the function body, the return value is unit by default
Functions can be declared by direct quantity. The syntax is: (parameter list) = > {function body} If the function has only one line, curly braces can be omitted
If there is only one parameter and there is no ambiguity, the parentheses of the parameter list can not be written
The declaration method of function direct quantity has no method name and cannot be called directly This definition is mainly used for function values and higher-order functions
Function is a first-class citizen of scala and has complete functions:
- Become a member of a class
- Assign to a variable
- Pass it as an argument to another function
- Returns another function from one function
Function as a member of a class
Like java, a function can be used as a member of a custom class to describe some behavior of the class
class Person { def eat(food: String): Unit = { println("eating " + food) } } object Main { def main(args: Array[String]): Unit = { val p = new Person() p.eat("Apple") } }
Function assigned to a variable
A function can be assigned to a variable (including a constant) like a general value This function can then be called through a variable
val add = (x: Int, y: Int) => x + y println(add(1, 2))
Direct variables are usually used when assigning values to a variable
Defining methods in methods can achieve similar results:
object Hello { def main(args: Array[String]): Unit = { val test = new Test println(test.foo()) } } class Test { def foo(): String = { def hello(): String = { "hello, world" } hello() } }
We can even define a function first and then assign it to a variable, which requires writing "" in the parameter Indicates that the parameters are specified at the time of subsequent calls:
object Hello { def sum(x: Int, y: Int, z: Int): Int = { x + y + z } def main(args: Array[String]): Unit = { val sumFun = sum(_, _, _) println(sumFun(1, 2, 3)) } }
"val sumFun = sum(_, _, _)" It can be simplified to "val sumfun = sum"
When defining function variables, you can specify some determined parameters first, and use "_" for undetermined parameters:
object Hello { def sum(x: Int, y: Int, z: Int): Int = { x + y + z } def main(args: Array[String]): Unit = { val sumFun = sum(_: Int, _: Int, 6) println(sumFun(2, 4)) } }
Use "" Some very flexible function calls can be implemented
Higher order function
Higher order functions are functions that can be passed as arguments to or returned from another function Super flexible programming can be realized by using high-order functions This is similar to the function pointer of C language, but it is safer
Let's first look at using a function as a parameter, which requires a clear declaration of the return value and parameters of the function, which is defined by "(paramtypes) = > (ReturnType)". For the contents in parentheses, you only need to write the type of the parameter. The following is a simple example:
def operate2Int(x: Int, y: Int, opera: (Int, Int) => Int): Int = { opera(x, y) } val add = (x: Int, y: Int) => x + y println(operate2Int(5, 1, add))
If you want to return a function, you also need to fully declare the style of the returned function when declaring the return value type:
def opt2NumFun(funName: String): (Int, Int)=>Int = { funName match { case "add" => (x, y) => x + y case "sub" => (x, y) => x - y case "mul" => (x, y) => x * y case "div" => (x, y) => x / y case _ => (_, _) => 0 } } println(opt2NumFun("add")(5, 1)) println(opt2NumFun("sub")(10, 5)) println(opt2NumFun("mul")(3, 10)) println(opt2NumFun("div")(30, 3))
The parameter type can be omitted when returning, because the structure of the return function has been declared at the time of definition The type of parameter can be inferred automatically
When returning a function, there is a simpler way to write it If the parameters of the function body call of the function you return are called in sequence and each parameter is used only once, you can omit the parameter list declaration and change all parameters to ""
The above opt2NumFun can be simplified to:
def opt2NumFun(funName: String): (Int, Int)=>Int = { funName match { case "add" => _ + _ case "sub" => _ - _ case "mul" => _ * _ case "div" => _ / _ case _ => (_, _) => 0 } }
placeholder
A lot has been covered before using the placeholder "" As an example, let's make a simple summary
You can use "" in the function direct quantity Use as one or more parameters so that you no longer have to declare a function's parameter list
For example, the add function can be simplified to:
val add = (_:Int) + (_:Int) println(add(10, 1))
add is still a function, but the parameter list and return value do not need to be declared The premise of using this method is that all parameters of the function are used once and in strict order of declaration
"_" If the type can be inferred, the type declaration can be omitted, otherwise the type must be declared
For example, the following situation can automatically infer "" Type of:
def opt3Num(func: (Int, Int, Int) => Int, x: Int, y: Int, z: Int): Int = { func(x, y, z) } println(opt3Num(_ + _ + _, 1, 3, 4)) println(opt3Num(_ + _ * _, 1, 2, 5)) println(opt3Num(_ / _ / _, 30, 10, 3))
The above code can be further simplified:
val opt3Num = (_:(Int, Int, Int)=>Int)(_: Int, _: Int, _: Int) println(opt3Num(_ + _ + _, 1, 3, 4)) println(opt3Num(_ + _ * _, 1, 2, 5)) println(opt3Num(_ / _ / _, 30, 10, 3))
Placeholders can also replace all or part of the parameter list when passing a function to a variable This has been demonstrated before. I won't demonstrate it again
closure
Closure is an important concept Programming languages with high - order functions generally have closure
If an external variable is used in the definition of a function Because it is a variable, the value of this variable is uncertain when calling the function If an external variable is used when calling a function, the value of the variable is brought into the function to become the internal value of the function. This process is called closure
Closures can cause some problems Because a function uses closures to get the value of an external variable only when it needs an external variable However, the external variable may change at any time during the function call. In this way, if the function uses the external variable once and uses it again after a while, the value of the external variable used in the two times may be different
Similarly, closures may change the value of external variables, which may also change the use of this variable by other functions, resulting in undefined behavior
Moreover, because functions may hold references to external variables, external variables may not be released by GC, which may lead to memory leakage
In development, we should try to avoid the harm caused by closures For example, when referring to external data inside a function, you should try to refer to constants instead of variables
Variable parameters
In scala, you can make the last parameter repeatable, so that the parameters of the function can be changed The writing method is to add an "*" after the parameter
def add(nums: Int*): Int = { var sum = 0 for (num <- nums) { sum += num } sum } println(add(1, 2, 3)) println(add(4, 5, 6, 7, 8))
Variable parameters are treated as arrays
Tail recursion
In Scala, you should try to use recursion to replace loops But the efficiency of recursion is not high
If a recursive call occurs only in the last expression of a function, scala can optimize the recursion Because this recursive call does not need to use the result of the last call, it can be transformed into a simple iteration at the bottom without pressing the stack
For example, calculate the sum of even numbers from 1 to 100, and if it is greater than 100, the call ends, which can be optimized into a tail recursion:
def foo(x: Int, sum: Int): Int = { if (sum > 100 || x > 100) { sum } else if (x % 2 == 0) { foo(x + 1, sum) } else { foo(x + 1, sum + x) } } println(foo(1, 0))
Because every recursion occurs in the last statement of the function, this is a tail recursion
Custom control structure
scala uses high - order functions and Coriolis to implement custom control structures
Higher order functions are described above Coriolism is the process of splitting the parameter list of a function into multiple parameter lists
Here is a simple "add and print" function:
def addAndPrint(num1: Int, num2: Int, pf: (Int)=>Unit): Unit = { val sum = num1 + num2 pf(sum) } addAndPrint(5, 7, println)
We can actually split addAndPrint Because we found that the parameter list of this function consists of two types: one is two numbers for increasing; The other is a print function:
def addAndPrint(num1: Int, num2: Int)(pf: (Int)=>Unit): Unit = { val sum = num1 + num2 pf(sum) } addAndPrint(5, 7)(println)
The parameter list of addAndPrint becomes two When calling, you also need to pass two parameters
When high - order functions and Coriolis are used together, a custom control structure can be constructed After coritization of a function, if a parameter has only one value to pass, you can rewrite the parentheses into curly braces
For example, addAndPrint above can be called as follows:
addAndPrint(10, 12) { println(_) }
It's like a control structure In fact, the essence is the call of a higher-order function
The foreach of List is a custom controller that allows us to do some behavior for each number of a List For example, the following code will print each element of the List after adding 10:
val li = List(1, 3, 5, 10) li.foreach { (num: Int)=> { println(num + 10) } }
The above code can be simplified to:
val li = List(1, 3, 5, 10) li.foreach { println(_) }
OOP
scala is a multi paradigm programming language, so in addition to functional programming, scala also supports OOP
The definition of scala class is similar to that of java Class can declare classes, which can contain properties and methods, and instantiate objects through new All members and methods are public by default. The permissions can be changed by using private and protected Scala does not have default permission
scala cannot define static members Because scala believes that static members will destroy the encapsulation of OOP Static members are separated from the object level and are the product of procedural programming. Moreover, if multiple threads seize a static member, it will bring thread safety problems Static members will always occupy memory and will never be released by GC
If you need static functions in scala, you can use singleton objects to implement them
Singleton object
Using object, you define a singleton object The method of using singleton object does not need to create object, but uses singleton object point method call The effect is like a static member
Scala will automatically help us instantiate the singleton and ensure that it is globally unique. We can call the methods of the singleton without specifying new
Singleton objects can exist alone or bound to a class When a singleton object and a class are written in the same file with the same name, they are bound to each other A singleton object is a companion object of a class The two can access each other's private members
When we call a method in a singleton object, we do not need to instantiate, but when we call a method in a class, we must instantiate the object
For example:
class Person { def eat(): Unit = { println("Have a meal") } } object Person { def sleep(): Unit = { println("Sleep sleep") } }
If we want to call the sleep() method, we can call it directly, but the eat() method must be instantiated and then called:
Person.sleep() val p = new Person p.eat()
The above implements a function similar to static
The entry program must be written in a singleton object The singleton object is stored in a special predef package This package will be imported automatically by scala Therefore, we use singleton objects without additional import packages
class
The format of classes defined in scala is as follows:
class class_name [(construct_list)] { class_body }
Construction parameters can be omitted. If there is no class body, curly braces can be omitted
Unlike java, the constructor is placed after the class name, and the constructor body is written directly in the class body Places that are neither properties nor methods are automatically interpreted as constructor bodies
The variables in the construction parameter list will be automatically added to the properties of the class. We don't need to write "this.xxx=xxx" like java These properties are private by default
Here is a simple demonstration:
object Test { def main(args: Array[String]): Unit = { val p = new Person("zhang", 20) p.show() } } class Person (name: String, age: Int) { println("construct a person") def show(): Unit = { println("name = " + name + ",age = " + age) } }
The output of this code is:
construct a person name = zhang,age = 20
If you want to provide multiple constructors, you need to define an auxiliary constructor, which is defined separately Defined as "def this()" The first action of each auxiliary constructor in scala is always to call other constructors, either the main constructor or other auxiliary constructors
That is, the main constructor will be called anyway
object Test { def main(args: Array[String]): Unit = { val p1 = new Person() val p2 = new Person("zhang") } } class Person (name: String, age: Int) { def this() { this("default", 0) println("call this()") } def this(name: String) { this(name, 0) println("call this(String)") } println("construct a person") }
Code output:
construct a person call this() construct a person call this(String)
Override and reload
scala supports overriding, that is, overriding the method of the parent class. You only need to add override in front of the method definition This syntax is similar to C #
If the method does not add override, an error will be reported For example:
class Fruit { def eat(): Unit = { println("eat fruit") } } class Apple extends Fruit { override def eat(): Unit = { println("Eat apples") } }
We can override the toString method as a matter of course:
object Test { def main(args: Array[String]): Unit = { val apple = new Apple println(apple) } } class Apple { override def toString: String = "A little apple" }
The usage of overloading is similar to java. You can define multiple methods with the same name, which will not be demonstrated here
Nonparametric method
In scala, parameterless methods are a bit special A parameterless method can actually be an attribute, and an attribute can also be a parameterless method
object Test { def main(args: Array[String]): Unit = { val p = new Person println(p.say1) println(p.say2()) println(p.say3) } } class Person { def say1 = "LOL" // The above wording is the same as the following def say2(): String = { "LOL" } // Same as below val say3 = "LOL" }
If a method is parameterless, it is the same as a general attribute to the outside So LOL is printed three times. In fact, in scala, attributes also exist as functions The above say3 will actually be converted to say1
This also proves that in scala, everything is a function
If you omit parentheses when defining a parameterless function, you cannot omit parentheses when calling If omitted, you can omit the parentheses
The following strategy is generally used to decide whether to add parentheses to the parameterless method:
- If the method is closed and has no external impact, it is best to omit the parentheses In this way, external callers can treat it as an attribute to simplify the programming model
- If the method is not closed and has an impact on the outside, such as reading files, it is best to add parentheses If you don't add it, it will make people wonder: just using an attribute leads to the change of the external environment
package
Similar to java,scala allows classes to be stored separately to realize the function of distinguishing namespaces Like java, you can use the package keyword to specify the package where the current code is located
However, the package of scala has nothing to do with the actual storage location of classes, so the concept of package here is actually different from that of java, which is very similar to that of C + + and c#'s namespace But the compiled class file will really be stored in the package folder
Then, like C + + and c#, scala allows multiple packages to be declared in the same file:
package cn { package lazycat { // The package here is CN lazycat } package hi { // The package here is CN hi } }
However, it is generally not recommended to write this, which will lead to very chaotic package management It is also recommended to declare only one package in a file
To import a package, use import Unlike java, import can appear anywhere in the code (java only allows import to appear in front of the code)
If you want to reference everything under a package, use "import xx.xx." All right We can even use an import to batch import some packages:
import java.util.{List, Map}
This is more convenient than java. java needs to write multiple imports to complete the import of multiple members
We can even alias some imported classes:
import java.util.{SimpleDataFormat => Sdf}
In this way, when instantiating this class object below, you can replace it with an alias:
val s: Sdf = null
Additional access rights
In addition to public, private and protected, Scala provides more detailed permission control. We can add a bracket after the permission statement and add some additional package names allowed to access (much like friends in C + +), for example:
private [cn.hhh] val f: String
So on CN HHH package, even if it is not in this class, you can directly access the f attribute
abstract class
Like java, scala allows the definition of abstract classes, the methods of abstract classes are not allowed to be implemented, and abstract classes are not allowed to be instantiated. These are standard configurations of OOP
abstract class Fruit { def eat() // This is an abstract method. If the subclass is not an abstract class, it must be implemented def get(): Unit = println("Got a fruit!") } class Apple extends Fruit { override def eat(): Unit = println("Ate an apple") }
Call parent class construction
If a subclass inherits the parent class, it needs to call the construction of the parent class, which needs to be declared at the time of inheritance. You can use the construction of the subclass to initialize, for example:
object Test { def main(args: Array[String]): Unit = { val r = new Teacher("Tang") println(r) } } class Person (name: String) class Teacher (name:String) extends Person(name) { override def toString: String = "teacher's name = " + name }
polymorphic
Polymorphism is the same as java, which is completed through upward transformation For example:
object Test { def main(args: Array[String]): Unit = { val f1: Fruit = new Apple val f2: Fruit = new Orange f1.eat() f2.eat() } } abstract class Fruit { def eat() } class Apple extends Fruit { override def eat(): Unit = println("Eat apples") } class Orange extends Fruit { override def eat(): Unit = println("Eat oranges") }
final
If final is added before the definition class, the class cannot be inherited:
final class class_name { ... }
For methods, the representation method cannot be overridden, which is the same as java
Inheritance structure
As we know, all classes in java inherit from the Object class
In Scala, all classes inherit from the Any class It includes the following methods:
- final def ==(that: Any): Boolean
- final def !=(that: Any): Boolean
- def equals(that: Any): Boolean
- def hashCode: Int
- def toString: String
These five methods can be found in java
Any has two direct subclasses, AnyValue and AnyRef The subclass of AnyValue is introduced earlier, in addition to the eight basic types of String plus a Unit These types cannot be new, only direct quantity can be used; Except for these nine classes, all other classes are from AnyRef Therefore, AnyRef is equivalent to the Object class of java
There is also a scala Null, all reference types are ancestors of this type So the following is written:
val p: Person = null
It can be explained by the upward transformation of OOP (because null must be the ancestor of Person)
However, except for the eight basic types of String, null assignment cannot be used:
val i: Int = null // Error!!!
Because Null is not the ancestor of Int
scala also has a scala Nothing indicates that there is no value When the program throws an exception, it will return this thing It is the descendant of all types
The inheritance structure tree of Scala can be represented by the following figure:
Trait
It can be compared with the Java interface, but it is quite different from the interface In Scala, a class can't be said to implement Trait, but rather a class mixed with Trait
Traits can include abstract and non - abstract methods But unlike abstract classes, subclasses can be mixed with multiple characteristics; Only one subclass of an abstract class can inherit
Use with to blend in traits Traits generally make classes conform to some characteristics If you want to mix in traits, you must have existing extensions, otherwise an error will be reported This is because scala gives you priority to use abstract classes. Only when abstract classes can't satisfy you can you use traits For example:
trait Eatable { def eat() } trait Walkable { def walk(): Unit = println("walk!") } abstract class Person { def talk() } class Student extends Person with Eatable with Walkable { override def talk(): Unit = println("The students are talking!") override def eat(): Unit = println("Students eat!") } object Test { def main(args: Array[String]): Unit = { val student: Student = new Student val eat: Eatable = student val walk: Walkable = student val person: Person = student eat.eat() walk.walk() person.talk() } }
This trait is a bit like inheritance Because traits can achieve specific methods
If there is a concrete method with the same name in the mixed characteristics, it may conflict after mixing, and the subclass must override this method
aggregate
array
A fixed length, linear data structure The bottom layer of Scala is implemented by java arrays
Scala provides two types of arrays: Array and ArrayBuffer Among them, the length of the former is fixed and the length of the latter can be changed. Some data can be added and deleted, but the efficiency is low
Common operations for arrays are as follows:
// Define an array of length 3 val arr1 = new Array[String](3) // Initialize array directly val arr2 = Array[String]("abc", "123", "456") // Without declaring generics, different types of data can be stored val arr3 = Array("a", 1, 3, 9.0, "b") // Use parentheses to access arrays println(arr3(2)) // Traversal array for (str <- arr2) { println(str) } // Modify element arr1(1) = "niubi" println(arr1(1))
Array has some useful API s. For example, concat can splice two arrays, and foreach can do something for each element of the array:
val arr1 = Array[Int](1, 2, 3, 4) val arr2 = Array[Int](5, 6, 7) val arr3 = Array.concat(arr1, arr2) arr3.foreach { println(_) }
There are many other API s, you can consult the documentation yourself
list
List has list and ListBuffer, of which the first is immutable and the second is variable
The definition List can be written as follows:
val list1 = List(2, 3, 5) val list2 = "a" :: "b" :: "c" :: "d" :: "e" :: Nil
The second way of writing is to splice a list in order, and the last one must be Nil The '::' actually inserts an element at the first position in the list But the operand is an R - value
The following common methods can manipulate the elements of a List:
val list1 = List(2, 3, 5) val list2 = "a" :: "b" :: "c" :: "d" :: "e" :: Nil println(list2(2)) println(list2.head) println(list2.tail) println(list1.sum) // Linked list val list3 = list1 ::: list2 println(list3) // Insert element backward val list4 = list2 :+ "f" println(list4) // Insert element forward val list5 = "z" +: list2 println(list5) // Delete element val list6 = list2.drop(1) println(list6) // Modify element val list7 = list2.updated(2, "a") println(list7)
Unlike java, the modification of List is realized by returning another List That is, the elements of the List itself cannot be changed
Variable lists and arrays
The above Array and List do not change themselves, but return a new collection, which is a waste of space
ArrayBuffer and ListBuffer can be changed, and can be converted to Array and List
val buffer = ArrayBuffer(4, 1, 4) buffer.append(10, 20) val arr = buffer.toArray println(arr)
The usage of ListBuffer is the same
Range
Range indicates the range of a data The to operator can be used to construct, and range can be converted to array or list:
val r = 1 to 10 val list = r.toList println(list)
Vector
In order to improve the random reading efficiency of List, Scala introduces a new set
The usage of Vector as like as two peas of List.
Set
The characteristic of Set is that the stored elements are not allowed to be repeated and are out of order The usage is similar to List
Map
Map is used to save key value pairs Map elements cannot be changed. Common operations are as follows:
// Create Map val m1 = Map(1 -> "zhang", 2 -> "Li", 3 -> "Wang") // Append element val m2 = m1 + (4 -> "Zhao") println(m2) // Delete element val m3 = m1 - 2 println(m3) // Get element println(m1.getOrElse(1, null)) // Traverse Map for (key <- m1.keySet) { println(key + "," + m1.getOrElse(key, null)) } // or val ite = m1.iterator for (t <- ite) { println(t._1 + "," + t._2) } // or m1.foreach(t => println(t._1 + "," + t._2))
You can generate a Map through zip:
val l1 = List(1, 2, 3) val l2 = List("a", "b", "c") val map = l1.zip(l2).toMap println(map)
This can combine other lists into a Map
Tuple
Represents a key value pair. A key can have multiple values A Tuple can be declared through parentheses, and the value can be obtained through "#":
val t = (1, "a", "b", 200) println(t._1) println(t._2) println(t._3) println(t._4)
Tuple stores up to 22 fields
Set general method
The basic collection objects contain the following methods (the following T indicates the type of collection storage element):
- Exist (t = > Boolean): for a function, each time it receives the value in the collection, return true to indicate that the element exists and false to indicate that the element does not exist As long as one of all elements returns true, the whole function returns true
- Sorted: returned after sorting the elements, sorted in ascending order
- Sortwith ((T, t) = > Boolean): specify the sorting rules yourself
- distinct: de duplication
- reverse: reverses the entire collection
- Reversemap (t = > unit): indicates that the element is reversed after some processing
- contains(T): whether to include an element
- startsWith(Seq[T]): whether to start with a collection
- endsWith(Seq[T]): whether to end with a set
In addition, there are some general higher-order functions in the set:
- Partition (t = > Boolean): split the collection according to a certain feature
- Map (t = > unit): apply the function to each element to get a new set
- Filter (t = > Boolean): filter the set according to a certain condition
- Filternot (t = > Boolean): filter unqualified elements
- Reduce ((T, t) = > t): apply two elements in the set to a function, and finally return a value
- par: allows multithreading when calculating elements in a collection
- Groupby (t = > any): grouping. Each element acts on the function and returns the same. It is divided into a group
Other features
generic paradigm
Scala is as like as two peas in Java. The only difference is that Scala uses brackets.
lazy
Usually, attributes declared with val or var allocate space directly However, if this variable is used later, the space is occupied all the time, which is a waste
When defining a variable, we can add a lazy before it, so that we can allocate space for this variable only when we know that this variable is used:
lazy val str = "hello" ... // There is no space allocated here println(str) // Space is allocated for str here
Option
Option can be expressed as a result in scala, which can have or have no value It has two implementations: some and None Some indicates that some values are returned, and None indicates that there is no value
Option needs to receive a generic type indicating what type of value is returned
For example, implement a division. If the divisor is 0, return None:
def div(x: Double, y: Double): Option[Double] = { if (y == 0) { None } else { Some(x / y) } } println(div(20.0, 10.0).getOrElse(0.0)) println(div(10.0, 0).getOrElse(0.0))
The getOrElse() method receives a default value. If None is returned, the default value is returned; If Some is returned, the value of Some is returned
This avoids some exception handling
Sample class
The sample class is a special class This class must have construction parameters, and implements the serialization interface by default. It overrides the toString, equals and hashcode methods by default, and can directly generate objects without new
For example:
object Test { def main(args: Array[String]): Unit = { val p: Person = Person("zhang") println(p.getName) } } case class Person (name: String) { def getName: String = name }
If new is not used, Scala's internal factory is used to instantiate the object The purpose of this class is to speed up the writing of java bean classes