scala列表的介绍
scala中的List类和java中的list类是完全不同的,对于java中的ArrayList来说,可以向其中添加元素,亦或是删除元素。是可变的,但是scala中的list是不可变的,其大小和其中的元素都是不可变的。它是由链表来实现的,所以包含常用的`head`,`tail`,`isEmpty`方法。
当然scala也提供了可变列表`ListBuffer`,以满足可变的需求
`List`是后进先出(LIFO)的,是类似栈访问模式的最好实现。List的前置以及头尾访问的时候,时间复杂度是`o(1)`,当然,对于列表中的大多数元素的操作,时间复杂度都是`o(n)`
一、创建不可变列表
def opList() = {
// 创建不可变列表
// 方式1: List(1, 2, 3)
val list_m1 = 1 :: 2 :: 3 :: Nil
// 方式2: List(1, 2, 3)
val list_m2 = List(1,2,3)
// 方式3: List(1.0, 2.0, 33.0, 4000.0)
val list_m3 = List(1,2.0,33D,4000L)
// 方式4: List(1, 2.0, 33.0, 4000)
val list_m4 = List[Number](1,2.0,33D,4000L)
// 方式5:
// List(1, 2, 3, 4, 5, 6, 7, 8, 9)
val list_m5_1 = List.range(1,10)
// List(0, 2, 4, 6, 8) 第三个参数为步长
val list_m5_2 = List.range(0,10,2)
// 方式6:List(foo, foo, foo)
val list_m6 = List.fill(3)("foo")
// 方式7: List(1, 2, 3) 通过可变listBuffer进行隐士转换
val list_m7 = collection.mutable.ListBuffer(1,2,3).toList
// 方式8:List(f, o, o)
val list_m8 = "foo".toList
}
1.1 List 中添加元素
因为List是不可变的,所以无法添加新的元素进去,所以在不是用listBuffer的情况下,常规的做法是:
- 通过
::
方法将元素添加到列表的前面,然后将结果赋给一个新的list - 将list定义为
var
,然后将结果重新赋值给自己
def opList() = {
// list添加元素:
// 第一种方式:在已有的list前面追加元素并赋值给一个新的变量
// List(0,1, 2, 3, 4, 5, 6, 7, 8, 9)
val list_add_before = List.range(0, 10)
// List(11,0,1, 2, 3, 4, 5, 6, 7, 8, 9)
val list_add_after_1 = 11 :: list_add_before
// List(12,11,0,1, 2, 3, 4, 5, 6, 7, 8, 9)
val list_add_after_2 = 12 :: list_add_after_1
// List(13,12,11,0,1, 2, 3, 4, 5, 6, 7, 8, 9)
val list_add_after_3 = 13 +: list_add_after_2
// List(13,12,11,0,1, 2, 3, 4, 5, 6, 7, 8, 9,14)
val list_add_after_4 = list_add_after_3 :+ 14
// 第二种方式:将list定义为var,每次将结果重新赋值给自己
var list_add = List.range(0,10)
// List(11,0,1, 2, 3, 4, 5, 6, 7, 8, 9)
list_add = 11 :: list_add
// List(12,11,0,1, 2, 3, 4, 5, 6, 7, 8, 9)
list_add = 12 :: list_add
}
注意:
::
这个符号是向右关联的,也就是说,通过该方法每次新增元素都是在原来列表的左边进行添加- 上面第二种方式
var
增加元素并将结果赋值给自己的,该种方式其实还是创建了一个新的列表,只不过通过var的方式将原有的变量指向了新的引用,而并非是在原有的列表上进行了修改
1.2 List 中删除元素
同样,由于List的不可变的特性,所以无法删除其中的元素,通常的做法就是,通过`filter或filterNot`过滤掉其中不需要的元素,然后将结果赋值给一个新的变量
def opList() = {
// list删除元素:
// 方式一
// List(0,1, 2, 3, 4, 5, 6, 7, 8, 9)
val list_del_before = List.range(0, 10)
// List(6, 7, 8, 9)
val list_del_after = list_del_before.filter(_ > 5)
// 方式二
// List(0,1, 2, 3, 4, 5, 6, 7, 8, 9)
var list = List.range(0, 10)
// List(0,1, 2, 3, 4, 5)
list = list.filterNot(_ > 5)
}
二、创建可变列表
ListBuffer是一个基于列表的缓冲实现,前置和附加操作的开销都是常量时间,其他的大多数操作开线基本都是线性时间。所以如果想要通过索引的方式访问元素,则不适合用ListBuffer,更适合使用ArrayBuffer
def opListBuffer() = {
// 创建一个空的ListBuffer
val ListBufferStr_1 = new ListBuffer[String]()
// 创建一个非空的ListBuffer
val ListBufferStr_2 = ListBuffer(1,2,3,4)
}
2.1 ListBuffer 中添加元素
def opListBuffer() = {
// 创建一个空的listBuffer
val listBufferStr_1 = new ListBuffer[String]()
// 向其中添加单个元素
listBufferStr_1 += "Apple"
listBufferStr_1 += "Banana"
// 向其中添加多个元素
listBufferStr_1 += ("m1", "m2", "m3")
// 通过添加序列的方式添加元素
listBufferStr_1 ++= Seq("a1","a2","a3")
// 通过append添加
listBufferStr_1.append("ttt","aaaa")
}
2.2 ListBuffer 中删除元素
def opListBuffer() = {
// 创建一个空的listBuffer
val listBufferStr_2 = ListBuffer[String]("Apple", "Banana", "m1", "m2", "m3", "ttt", "aaaa")
// 删除其中的一个元素
listBufferStr_2 -= "Apple"
// 删除多个元素
listBufferStr_2 -= ("m1", "m2", "m3")
// 通过可变序列来删除所包含的元素
listBufferStr_2 --= Seq("m1", "m2")
// 通过remove的方式删除
// 删除指定索引(索引从0开始)处的元素
listBufferStr_2.remove(1)
// 从指定索引(索引从0开始)处开始删除多个元素,第一个参数:从索引(从0开始)出开始删除,第二个参数:删除几个元素
listBufferStr_2.remove(1,2)
}
三、列表的合并与连接
def opListConbine() = {
val list_1 = List(1, 2, 3, 4, 5, 6)
val list_2 = List(2, 3, 4, 7)
val list_add_1 = list_1 ++ list_2
val list_add_2 = list_1 ::: list_2
val list_add_3 = List.concat(list_1, list_2)
val listBuffer_1 = List(1, 2, 3, 4, 5, 6)
val listBuffer_2 = List(2, 3, 4, 7)
val listBuffer_add_1=listBuffer_1 ++ listBuffer_2
val listBuffer_add_2 = list_1 ::: list_2
val listBuffer_add_3 = List.concat(list_1, list_2)
}
四、列表的分割
所谓的列表分割指的是,按照一个算法将列表分为一个或多个不同的子列表
4.1 groupBy
该方法是按照给定的函数,将原有的列表进行切分,最终生成一个map,key函数计算的结果,value为对应的列表的元素
def groupBy(): Unit ={
// List(0,1,2,3,4,5,6,7,8,9)
val list = List.range(0, 10)
// Map(2 -> List(2, 6), 1 -> List(1, 5, 9), 3 -> List(3, 7), 0 -> List(0, 4, 8))
val rlist = list.groupBy(_%4)
}
4.2 grouped
该方法是按照给定的参数n,见原有的列表按照n个元素为一组,进行切分成多个列表,最终返回的是一个迭代器
def grouped(): Unit ={
// List(0,1,2,3,4,5,6,7,8,9)
val list = List.range(0, 10)
// Iterator(List(0, 1, 2), List(3, 4, 5), List(6, 7, 8), List(9))
val rlist = list.grouped(3)
}
4.3 partition
该方法是通过给定的判断函数,将原有的列表切分为两个列表,最终返回一个二元组
def partition(): Unit = {
// List(0,1,2,3,4,5,6,7,8,9)
val list = List.range(0, 10)
// (List(6, 7, 8, 9),List(0, 1, 2, 3, 4, 5))
val rlist = list.partition(_ > 5)
}
4.4 span
该方法会通过传入的判断函数依次判断每一个元素,当判断到某个元素,函数返回为false的时候,则在此数将序列进行切分为两个,跟partition函数的区别是:
- partition函数切分出的结果中,两个列表中的元素都是同一类,要么是符合指定函数的,要么是不符合指定函数的
- span函数切分出的结果中,第一个列表中的元素必定是符合指定函数的,但是第二个序列中就不一定了
def span(): Unit = {
// List(0,1,2,3,4,5,6,7,8,9)
val list = List.range(0, 10)
// (List(0, 1, 2, 3, 4),List(5, 6, 7, 8, 9))
val rlist = list.span(_ < 5)
// (List(),List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9))
val rlist1 = list.span(_ > 5)
}
4.5 splitAt
该函数会根据给定的下标将列表切分为两个列表,最终返回的一个二元组
def splitAt(): Unit = {
// List(0,1,2,3,4,5,6,7,8,9)
val list = List.range(0, 10)
// (List(0, 1),List(2, 3, 4, 5, 6, 7, 8, 9))
val rlist = list.splitAt(2)
println(rlist)
}
4.6 sliding
该函数会根据给定的size和setp将列表进行切分为多个列表,最终个返回一个迭代器,当size > 1 && size==setp
的时候,该函数等同于grouped(n)函数
def sliding(): Unit = {
// List(0,1,2,3,4,5,6,7,8,9)
val list = List.range(0, 10)
// Iterator(List(0, 1, 2), List(3, 4, 5), List(6, 7, 8), List(9))
val rlist = list.sliding(3,3)
}
4.7 unzip
该方法主要是针对二元组序列,会将二元组序列中的第一个元素与第二个元素分别组成两个列表进行返回,最终返回的也是一个二元组
def unzip(): Unit = {
// List(0,1,2,3,4,5,6,7,8,9)
val list = List((1,"a"),(2,"b"),(3,"c"),(4,"d"),(5,"e"))
// (List(1, 2, 3, 4, 5),List(a, b, c, d, e))
val rlist = list.unzip
}
五、列表的惰性版本:Stream
Stream和List很像,不同之处在于,它的元素是惰性计算的,所以一个Stream可以是无限长,只有在其中的元素被访问的时候才计算。除此之外,其他的行为和List类似
4.1 Stream的创建
def opStream() = {
// 方式1: 使用 #:: 连接符,并且结尾必须使用 Stream.Empty
// Stream(1, ?) 结果的结尾用?表示,这是因为流的结尾还没有执行
val stream = 1 #:: 2 #:: 3 #:: Stream.Empty
// 方式2: 使用toStream的方式
// Stream(1, ?)
val stream2 = (1 to 1000000000).toStream
}
4.2 Stream元素的访问
def opStream() = {
// Stream(1, ?)
val stream1 = (1 to 1000000000).toStream
// 访问头元素 1,立即返回
val head = stream1.head
// 访问尾部元素 Stream(2, ?) ,返回?说明集合的尾部尚未被执行
val tail = stream1.tail
// Stream(1, ?)
val take3 = stream1.take(3)
// Stream(101, ?)
val filte1 = stream1.filter(_ > 100)
// Stream(1, ?)
val filter2 = stream1.filter(_ < 100)
// Stream(2, ?)
val mapstr = stream1.map(_ * 2)
}
以上这些方式都属于变换方法,他们属于集合的一些方法,是基于已有的算法,将其应用到给定的输入集合,产生新的输出集合,特别注意,在调用非变换方法的时候要小心一点,很可能会出现java.lang.OutOfMemoryError
变换方法例如:
- stream.filter
- stream.map
- stream.reverse
非变换方法例如:
- stream.max
- stream.size
- stream.sum