数组 |
元组 |
映射 |
列表 |
集合是一种用来存储各种对象和数据的容器。Scala 集合分为可变的和不可变的集合。
1. 不可变集合可以安全的并发访问。 2. 可变集合可以在适当的地方被更新或扩展。这意味着你可以修改,添加,移除一个集合的元素。
不可变集合,相比之下,永远不会改变。不过,你仍然可以模拟添加,移除或更新操作。但是这些操作将在每一种情况下都返回一个新的集合,同时使原来的集合不发生改变。
scala集合两个主要的包:
# 不可变集合 scala.collection.immutable (Scala默认采用不可变集合) # 可变集合 scala.collection.mutable
Scala的集合有三大类:序列Seq、集Set、映射Map,所有的集合都扩展自Iterable特质(暂理解为接口),意味着集合的基本特点是支持迭代遍历的。
分类 | 描述 |
---|---|
Seq | 序列。元素以线性方式存储,集合中可以存放重复对象,。参考API文档 |
Set | 集(数据集,区别于集合)。集中的对象不按特定的方式排序,并且没有重复对象。 参考 API文档 |
Map | 一种把键对象和值对象映射的集合,它的每一个元素都包含一对键对象和值对象。 ?参考 API文档 |
对于可变与不可变集合,Seq、Set、Map又有不同的实现方式,下二图详细描述了其继承关系。官方文档
不可变集合结构:
可变集合结构图:
数组元素内容要求类型一致。按照是否可扩容分为两种:
# Array - 定长数组,数组不可扩容 scala.Array # ArrayBuffer - 变长数组,数组可扩容 scala.collection.mutable.ArrayBuffer
Java中创建定长数组
int[] data = new int[3]; /*定义长度为3的数组*/ data[0] = 1; // 第一个元素 data[1] = 2; // 第二个元素 data[2] = 3; // 第三个元素 ? //也可以创建时初始化数组元素 int[] data=new int[]{1,2,3};
同样,Scala中也有两种创建一个定长数组的方式:
使用new关键字创建一个定长数组,var arr=new Array[Int](3)
直接使用Array创建并初始化一个数组,注意不再使用new关键字。var arr=Array(1,2,3)
课上练习:使用Array定义一个长度不变的数组
object ArrayDemo {
?def main(args: Array[String]){
? ?//初始化一个长度为8的定长数组
? ?val arr1 = new Array[Int](8)
? ?//会有初始化零值:Array[Int] = Array(0,0,0,0,0,0,...)
? ?//直接打印定长数组,内容为数组的hashcode值
? ?println(arr1)
? ?//将数组转换成数组缓冲,就可以看到原数组中的内容了
? ?//toBuffer会将数组转换长数组缓冲
? ?println(arr1.toBuffer)
?
? ?//注意:如果不使用new获取数组,相当于调用了数组的apply方法,直接为数组赋值
? ?//通过一组初始化值定义定长数组
? ?val arr2 = Array[Int](10,20,30)
? ?//输出数组元素值
? ?println(arr2.toBuffer)
? ?//使用()来访问元素
? ?println(arr2(2))
? ?//遍历数组
? ?for(i <- 0 until arr2.length)
? ? ?print(s"$i:${arr2(i)} ")
? ?println()
? ?
? ?//赋初值的字符串数组
? ?val strs1 = Array("hello" ,"world")
? ?//访问并修改元素值
? ?strs1(0) = "byebye"
? ?for(i <- 0 until strs1.length)
? ? ?print(s"$i:${strs1(i)} ")
? ? ?println()
}
}
运行结果如下:
[I@4520ebad ArrayBuffer(0, 0, 0, 0, 0, 0, 0, 0) ArrayBuffer(10, 20, 30) 30 0:10 1:20 2:30 0:byebye 1:world
定长数组是不可变集合吗?
不是。定长数组是可变集合的一种,内容可变,但是其长度不可变。
# 扩展:为什么定长数组是可变集合? Array本身不属于scala集合成员,从前面类继承图中也可发现这一点,在可变集合图中IndexedSeq有一条虚线指向了Array,说明并不是直接继承关系。 一般将Array归为集合是因为Scala默认将Array隐式转换为WrappedArray,而WrappedArray实现了IndexedSeq特质。 从这一点上来说,String与WrappedString也有异曲同工之妙,可以发现在不可变集合中,String与IndexedSeq也是虚线连接,也就是说在Scala中,String可以当集合处理。参考下文中的描述,请自行通过源码验证。
使用 ArrayBuffer定义长度按需变化的数组。
import scala.collection.mutable.ArrayBuffer
?
object VarArrayDemo {
?def main(args: Array[String]){
? ?//定义一个空的可变长Int型数组
? ?val nums = ?ArrayBuffer[Int]()
?
? ?//在尾端添加元素
? ?nums += 1
?
? ?//在尾端添加多个元素
? ?nums += (2,3,4,5)
?
? ?//使用++=在尾端添加任何集合
? ?nums ++= Array(6,7,8)
?
? //这些操作符,有相应的 -= ,--=可以做数组的删减,用法同+=,++=
? ? ?
? //使用append追加一个或者多个元素
? ?nums.append(1)
? ?nums.append(2,3)
? ? ?
? //在下标2之前插入元素
? ?nums.insert(2,20)
? ?nums.insert(2,30,30) ? ?
?
? ?//移除最后2个元素
? ?nums.trimEnd(2)
? ?//移除最开始的一个或者多个元素
? ?nums.trimStart(1)
?
? ?//从下标2出移除一个或者多个元素
? ?nums.remove(2)
? ?nums.remove(2,2) ? ?
? ? ?
? //使用增强for循环进行数组遍历 ?
? ?for(elem <- nums)
? ? ?print(elem+" ")
? ? ?
? ?println()
? ?//基于下标访问使用增强for循环进行数组遍历
? ?for(i <- 0 until nums.length)
? ? ?print(nums(i)+" ")
}
}
执行结果:
2 30 4 5 6 7 8 1 ? 2 30 4 5 6 7 8 1
arr1.toBuffer ?//转为变长 arr2.toArray //转为定长
Scala Tuple表示固定元素的组合,元组可以装着多个不同类型的值,是不同类型的值的聚集。Tuple是Scala中非常重要的一种数据结构,后面会大量使用。其特点包括:
1.最多支持22个元素组合,分别对应类型Tuple1~Tuple22,相应也称为一元组(一般不用)、二元组、三元组... 2.元组可以容纳不同类型的元素 3.元组不可变
==特别地:==二元组可以表示Map中的一个元素,Map是K/V对偶的集合,对偶是元组的最简单形式。
创建元组:使用小括号()
将多个元素括起来,元素之间用逗号分隔,元素的类型和个数不超过22。
访问组元:使用_1
,_2
,_3
等形式访问组元,注意下标从1开始。
第二种方式定义的元组也可以通过a
,b
,c
去访问组元。
val t = ("qianfeng","scala",1) //定义元组 val t_1 = t._1 ? //取元组第一个值
另一种定义方式:
val tuple3 = new Tuple3(1, 3.14, "Fred")
元组的实际类型取决于它的元素的类型,比如 :
(99, "runoob")
实际类型是 Tuple2[Int, String]
('u', 'r', "the", 1, 4, "me")
实际类型 为 Tuple6[Char, Char, String, Int, Int, String]
目前 Scala 支持的元组最大长度为 22。对于更大长度你可以使用集合,或者样例类(case class)。
//注意元组元素的访问有下划线,并且访问下标从1开始 val value1 = tuple3._3 println(value1) // 按照索引访问元组的第一个元素,从0开始 val value2 = tuple3.productElement(0) println(value2)
因为元组本身不是集合成员,所以元组不能作为for生成器的表达式。但元组实现了productIterator()
方法,该方法可以生成元组的迭代器Iterator
(继承自TraversableOnce
),迭代器提供了遍历集合的方法。这也是通常我们将Tuple视为集合的原因。
方式1:
for (elem <- tuple1.productIterator) { ?print(elem) } println()
方式2:
t.productIterator.foreach(i => println(i)) //foreach是高阶函数 t.productIterator.foreach(print(_))
在Scala中,把哈希表这种数据结构叫做映射。Scala中的Map存储的内容是键值对(key-value),Map区分可变Map (scala.collection.mutable.Map) 和不可变Map(scala.collection.immutable.Map) 。不可变的Map(仅TreeMap)支持有序,而可变的Map是无序的。
Map中的元素为二元组,可用两种方式表示。
("a",1) "a"->1
课上练习
//构建一个不可变的Map,默认即为不可变Map
//其中的元素其实是Tuple2
val scores = Map("zhangsan"->90,"lisi"->80,"wangwu"->70)
?
//使用元组方式构建
val scores = Map(("zhangsan",90),("lisi",80),("wangwu",70))
?
//构建一个可变的map,注意包名
val scores = scala.collection.mutable.Map(("zhangsan",90),("lisi",80),("wangwu",70))
根据键获取map中对应的值,可以有以下三种方法,推荐使用getOrElse方法。
//如果key存在,则返回对应的值
//如果key不存在,则抛出异常[java.util.NoSuchElementException]
//在Java中,如果key不存在则返回null
val score1 = scores("lisi")
?
//使用contains方法检查是否存在key对应的值
//使用containts先判断在取值,可以防止异常,并加入相应的处理逻辑
// 返回Boolean,true或者false
// 如果key存在,则返回true
// 如果key不存在,则返回false
map4.contains("B")
?
val score2 = if(scores.contains("lisi")) scores("lisi") else 0
?
//使用get方法取值,返回Option对象,Some或者None
//如果返回some,可以进一步通过get方法取回相应的值
//如果返回None,通过get方法取值,抛出异常 java.util.NoSuchElementException: None.get
var map4 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3) )
println(map4.get("A")) //Some
println(map4.get("A").get) //得到Some在取出
?
//使用getOrElse()取值
//def getOrElse[V1 >: V](key: K, default: => V1)
//如果key存在,返回key对应的值。
//如果key不存在,返回默认值。
val score3 = scores.getOrElse("lisi",0)
遍历访问map
//修改键对应的值
//可变的map才能修改
//key存在,则修改对应的值,key不存在,则添加键值对
scores("lisi") = 100
println(scores)
scores.update("lisi",50)
println(scores)
?
//添加单个元素-方式1,如果key存在,则修改相应key的值。
scores("zhaoliu") = 88
println(scores)
?
//添加单个元素-方式2
scores +=("tom"->77)
println(scores)
?
//添加多个元素-方式1
scores = scores + ("tom"->77,"jerry"->88)
scores +=("tom"->77,"jerry"->88)
?
val scores2 = Map(("za",90),("lq",80),("wg",70)) ? ? ? ? ? ? ? ? ? ? ?
//添加多个元素-方式2
scores ++= scores2
?
//移除键值对
scores-"lisi"
?
//移除多个键一
scores--List("zhangsan","tom")
?
//移除多个键二
scores-("lisi","lq")
//遍历
//返回一个set集合
val res = scores.keySet
?
for(elem <- res)
? print(elem + " ")
?
//返回Map中所有key的迭代器 set结合
val ite = scores.keys
?
//返回Map中所有值的迭代器
val values = scores.values
?
//返回键值对
for (item <- scores)
? ?print(item+" ")
?
?
//使用k、v表示二元组中的键和值
for ((k,v) <- scores)
? print(k+":"+v+" ")
Scala 列表类似于数组,它们所有元素的类型都相同,但是它们也有所不同:列表是不可变的,值一旦被定义了就不能改变,其次列表是链表结构,而数组不是。
1.列表中的元素类型必须相同。 2.列表是有序的。 3.列表是不可变的,内容及长度都不可变。
object ImmutListDemo {
? ?def main(args: Array[String]) {
? ? ?//创建一个不可变的集合
? ? ?val lst1 = List(1,2,3)
? ? ?//将0插入到lst1的前面生成一个新的List ::和+:右结合
? ? ?// ::为右结合操作符
? ? ?// 将元素追加到集合开头
? ? ?val lst2 = 0 :: lst1
? ? ?val lst3 = lst1.::(0) ?//等价上诉写法
? ? ?val lst4 = 0 +: lst1
? ? ?val lst5 = lst1.+:(0) ?//等价上诉写法
? ? ?//将一个元素添加到lst1的后面产生一个新的集合,:+左结合
? ? ?val lst6 = lst1 :+ 3
? ? ?val lst0 = List(4,5,6)
? ? ?//将2个list合并成一个新的List ++ 左结合
? ? ?val lst7 = lst1 ++ lst0
? ? ?//将lst1插入到lst0前面生成一个新的集合 ++: 左结合
? ? ?val lst8 = lst1 ++: lst0
? ? ?//将lst0插入到lst1前面生成一个新的集合 ::: 右结合
? ? ?val lst9 = lst1.:::(lst0)
? ? ?println(lst9)
? ? ?println(lst1(2))
? }
}
/**
* List的各种遍历方式
*/
val lst = List(1,2,3,4,5);
print("foreach遍历:")
lst.foreach { x => print(x+",")} ?//foreach遍历,这个是传统遍历,新手不熟无奈之下可以用它
println("")
?
var temp = lst.map { x => x+1 } ? //遍历,与foreach的区别是返回值为List【B】
println("map遍历:"+temp.mkString(","))
对列表的所有操作可以表达为一下三种
head 返回列表的第一个元素。 tail 返回除第一个元素之外所有元素组成的列表。
isEmpty 返回列表是否为空。
注意:其中tail head作用在空列表上,会报异常。
val list0 = List(1,2,3)
val list1 = List(4,5,6)
// head:返回列表第一个元素
list0.head // Int = 1
// tail:返回除了第一个元素之外的其他元素,以列表返回
list0.tail // List[Int] = List(2, 3)
// isEmpty:判断列表是否为空,为空时返回true
list0.isEmpty // Boolean = false
// concat:连接列表,返回一个新的集合
List.concat(list0,list1) // List[Int] = List(1, 2, 3, 4,5, 6)
// fill:使用数个相同的元素创建一个列表
List.fill(10)(2) // 重复次数:10,重复元素:2
// reverse:将列表顺序反转,返回一个新的集合
list0.reverse
?
//求列表长度
list0.length
ListBuffer与List的区别在于:修改ListBuffer改变的是本身。
import scala.collection.mutable.ListBuffer
?
object MutListDemo extends App{
?//构建一个可变列表,初始有3个元素1,2,3
?val lst0 = ListBuffer[Int](1,2,3)
?//创建一个空的可变列表
?val lst1 = new ListBuffer[Int]
?//向lst1中追加元素,注意:没有生成新的集合
?lst1 += 4
?lst1.append(5)
?
?//将lst1中的元素最近到lst0中, 注意:没有生成新的集合
?lst0 ++= lst1
?
?//将lst0和lst1合并成一个新的ListBuffer 注意:生成了一个集合
?val lst2= lst0 ++ lst1
?
?//将元素追加到lst0的后面生成一个新的集合
?val lst3 = lst0 :+ 5
}
Set中的元素不可重复,无序(TreeSet除外)。
import scala.collection.immutable.HashSet
val set0=Set(1,2,3,4,5)
println(set0.getClass) //scala.collection.immutable.HashSet
?
val set1 = new HashSet[Int]() //默认的Set
// 可以使用加号追加元素,会产生一个新的集合,原有set不变
val set2 = set1 + 1
// Set集合中不会出现重复的元素
val set3 = set2 ++ Set(1,2,3)
val set4 = Set(1,2,3,4,5,6)
// Set集合中的元素是无序的
print(set4)
?
?
// SortedSet中是有序的
val set5=scala.collection.immutable.TreeSet(1,1,10,5,6,7,8,11,13)
import scala.collection.mutable
object MutSetDemo extends App{
??
? ?val mutableSet = Set(1,2,3)
? ?println(mutableSet.getClass.getName) // scala.collection.mutable.HashSet
?
? //创建一个可变的HashSet
? val set1 = new mutable.HashSet[Int]()
? //向HashSet中添加元素
? set1 += 2
? //add等价于+=
? set1.add(4)
? //删除一个元素,如果删除的对象不存在,则不生效,也不会报错
? set1 -= 5
? set1.remove(2)
? println(set1)
}