Scala里List的设计
Scala List
Scala中Array和List的区别
Scala API文档——List
Scala中列表非常类似于数组,这意味着,一个列表的所有元素都具有相同的类型,但有两个重要的区别。首先,列表是不可变的,这意味着一个列表的元素可以不被分配来改变。第二,列表表示一个链表,而数组是平坦的。
// List of Strings
val fruit:List[String] = List("apples","oranges","pears")
// List of Integers
val nums:List[Int] = List(1,2,3,4)
// Empty List:
val empty:List[Nothing] = List()
// Two dimensional list
val dim: List[List[Int]] =
List(
List(1,0,0),
List(0,1,0),
List(0,0,1)
)
所有的列表可以使用两种基本的构建模块来定义,一个无尾Nil
和::
,这有一个明显的缺点。Nil
也代表了空列表。
所有上述列表可以定义如下:
//List of Strings
val fruit = "apples"::("oranges"::("pears"::Nil))
//List of Integers
val nums = 1::(2::(3::(4::Nil)))
//Empty List.
val empty = Nil
//Two dimensional list
val dim = (1::(0::(0::Nil)))::
(0::(1::(0::Nil)))::
(0::(0::(1::Nil)))::Nil
::的使用
val a=List(1,2) //List[Int]=List[Int]
val b=List('a') //List[Char]=List('a')
val c=a::b //List[AnyVal] = List(1,2,a)
列表的基本操作
方法 | 描述 |
---|---|
head | 此方法返回的列表中的第一个元素 |
tail | 此方法返回一个由除了第一个元素外的所有元素的列表 |
isEmpty | 如果列表为空,此方法返回true,否则为false |
object Test{
def main(args:Array[String]){
val fruit = "apples"::("oranges"::("pears"::Nil))
val nums = Nil
println("Head of fruit : " + fruit.head) //apples
println("Tail of fruit : " + fruit.tail) //List(oranges,pears)
println("Check if fruit is empty : " + fruit.isEmpty) //false
println("Check if nums is empty : " + nums.isEmpty) //true
}
}
串联列表:::
可以使用:::
运算符或列表List.:::()
或List.concat()
方法来添加两个或多个列表。
object Test{
def main(args:Array[String]){
val fruit1 = "apples"::("oranges"::("pears"::Nil))
val fruit2 = "mangoes"::("banana"::Nil)
// use two or more lists with ::: operator
var fruit = fruit1 ::: fruit2
println("friut1 ::: friut2 : " + fruit)
//fruit1:::fruit2: List(apples, oranges, pears, mangoes, banana)
//use two lists with Set.:::() method
fruit = fruit1.:::(fruit2)
println("fruit1.:::(fruit2) : " + fruit)
//fruit1.:::(fruit2):List(mangoes, banana, apples, oranges, pears)
//pass two or more lists as arguments
fruit = List.concat(fruit1, fruit2)
println("List.concat(fruit1,fruit2): " + fruit)
}
//List.concat(fruit1,fruit2):List(apples,oranges,pears,mangoes,banana)
}
创建同一列表List.fill()
方法
可以用List.fill()
方法创建,包括相同的元素如下的零个或更多个拷贝的列表:
object Test{
def main(args:Array[String]){
val fruit = List.fill(3)("appears") // Repeats apples three times
println("fruit:" + fruit)
// fruit: List(apples,apples,apples)
val num = List.fill(10)(2) //Repeats 2,10 times.
println("num : " + num)
// num:List(2,2,2,2,2,2,2,2,2,2)
}
}
tabulate 制成表格
object Test{
def main(args:Array[String]){
//Create 5 elements using the given function.
val squares = List.tabulate(6)(n=>n*n)
println("squares: " + squares)
//List(0,1,4,9,16,25)
//
val mul = List.tabulate(4,5)(_*_)
println("mul : " + mul)
//mul : List(List(0,0,0,0,0),List(0,1,2,3,4),
List(0,2,4,6,8),List(0,3,6,9,12))
}
}
反向列表顺序
object Test{
def main(args:Array[String]){
val fruit = "apples"::("oranges"::("pears"::Nil))
println("Before reverse fruit : " + fruit)
// Before reverse fruit: List(apples, oranges, pears)
println("After reverse fruit : " + fruit.reverse)
// After reverse fruit: List(pears, oranges, apples)
}
}
Scala 列表方法
def ++[B](that:GenTraversableOnce[B]):List[B]
返回一个新List,包含左操作数,接着是右操作数中的元素,List的类型是两个操作数元素类型的超集。def ++:[B >: A, That](that: collection.Traversable[B])(implicit bf: CanBuildFrom[List[A],B,That]):That
,和::
不同的地方在于,右边操作数的类型决定了输出变量的类型。def +: (elem A):List[A]
,复制一个List,添加一个元素到List开头,原始列表不会被改变def /:[B](z:B)(op:(B,A)=>B):B
,应用一个二元操作符给start value和所有可遍历的迭代器元素,从左到右.z /: xs == xs foldLeft z
:+(elem A):List[A]
:val a=List(1);val b = a:+2;//b:List[Int]=List(1,2)
::(x,A):List[A]
,在列表的开始添加一个元素。:::(prefix:List[A]):List[A]
,List(1,2):::List(3,4)=List(3,4).:::List(1,2)=List(1,2,3,4)
def :\[B](z:B)(op:(A,B)=>B):B
,xs :\ z == xs foldRight z
def addString(b:StringBuilder):StringBuilder
,把列表中的所有元素转成一个string builder,不同于toString.def addStirng(b:StringBuilder,sep:String)
,val a=List(1,2,3,4);val b = new StringBuilder();a.addString(b,",,")//StringBuilder = 1,,2,,3,,4
def addString(b:StringBuilder, start:String, sep:String, end:String):StringBuilder
,val a=List(1,2,3,4);val b=new StringBuilder();a.addString(b,"LIst(",",",")")//StringBuilder=List(1,2,3,4)
def aggregate[B](z:=>B)(seqop:(B,A)=>B,combop:(B,B)=>B):B
def andThen
函数组合相关def apply(n:Int):A
,Select an element by its index in the sequence. Note, the execution of apply may take time proportional to the index value.(执行时间和index的大小成正比)applyOrElse[A1 <:Int, B1>:A](x:A1, default:(A1)=>B1):B1
偏函数canEqual(that:Any):Boolean
- ….还有茫茫多
List的链表结构
List是一个保证了前端入队和取尾集(tail)效率的不可变队列。
如:
val a = List(1,2,3)
val b = 4::a
b=List(1,2,3,4)
.,在这个赋值过程中,没有发生对象复制,b的内部表示可以理解为是 4 -> List(1,2,3)
,也就是4->a
.
其实,a的内部可以理解为 1->(2->(3->List()))
,这也解释了为什么List的元素访问复杂度是O(n)
.
List 与 Array (即使你不去改变Array内的元素)的主要区别是:
1. 它在API层面上保证了内部元素不变
2. 它取尾集(去掉头几个元素后剩下的List)的效率是很高的,非常适用基于模式匹配的迭代。
如:
def foo(I:List[Int])=>match I{
case x::xs => x + foo(xs)
case _=> 0
}
如果用Array,每次递归都会导致n-1个元素被复制。如果在Java里,一般只能通过额外Index或Iterator来进行迭代。而index本身,就成了副作用,你必须关注某个动作应该在index变化之前还是之后做。
为什么列表不支持 append ?
类 List 没有提供 append 操作,因为随着列表变长 append 的耗时将呈线性增长,而使用 :: 做前缀则仅花费常量时间。如果你想通过添加元素来构造列表,你的选择是把它们前缀进去,当你完成之后再调用 reverse ;或使用 ListBuffer ,一种提供 append 操作的可变列表,当你完成之后调用 toList 。