scala集合_数组_元组_映射_列表

|----|
| 数组 |
| 元组 |
| 映射 |
| 列表 |

1.11 集合(scala.collection)

集合是一种用来存储各种对象和数据的容器。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又有不同的实现方式,下二图详细描述了其继承关系。官方文档

不可变集合结构:

可变集合结构图:

1.11.1 数组

数组元素内容要求类型一致。按照是否可扩容分为两种:

复制代码
# Array
- 定长数组,数组不可扩容 scala.Array
# ArrayBuffer
- 变长数组,数组可扩容 scala.collection.mutable.ArrayBuffer
1.11.1.1 定长数组Array

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定义一个长度不变的数组

sql 复制代码
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可以当集合处理。参考下文中的描述,请自行通过源码验证。
1.11.1.2 变长数组ArrayBuffer

使用 ArrayBuffer定义长度按需变化的数组。

Scala 复制代码
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
1.11.1.3 定长数组与变长数组的转换
复制代码
arr1.toBuffer  //转为变长
arr2.toArray     //转为定长
1.11.2 元组(Tuple)

Scala Tuple表示固定元素的组合,元组可以装着多个不同类型的值,是不同类型的值的聚集。Tuple是Scala中非常重要的一种数据结构,后面会大量使用。其特点包括:

复制代码
1.最多支持22个元素组合,分别对应类型Tuple1~Tuple22,相应也称为一元组(一般不用)、二元组、三元组...
2.元组可以容纳不同类型的元素
3.元组不可变

==特别地:==二元组可以表示Map中的一个元素,Map是K/V对偶的集合,对偶是元组的最简单形式。

1.11.2.1 创建访问元组

创建元组:使用小括号()将多个元素括起来,元素之间用逗号分隔,元素的类型和个数不超过22。

访问组元:使用_1_2_3等形式访问组元,注意下标从1开始。

第二种方式定义的元组也可以通过abc 去访问组元。

复制代码
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.11.2.2 元组访问
复制代码
//注意元组元素的访问有下划线,并且访问下标从1开始
val value1 = tuple3._3
println(value1)
// 按照索引访问元组的第一个元素,从0开始
val value2 = tuple3.productElement(0) 
println(value2)
1.11.2.3 元组遍历

因为元组本身不是集合成员,所以元组不能作为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(_))
1.11.3 映射(Map)

在Scala中,把哈希表这种数据结构叫做映射。Scala中的Map存储的内容是键值对(key-value),Map区分可变Map (scala.collection.mutable.Map) 和不可变Map(scala.collection.immutable.Map) 。不可变的Map(仅TreeMap)支持有序,而可变的Map是无序的。

1.11.3.1 构建映射

Map中的元素为二元组,可用两种方式表示。

复制代码
("a",1)
"a"->1

课上练习

Erlang 复制代码
//构建一个不可变的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))
1.11.3.2 访问映射中的值

根据键获取map中对应的值,可以有以下三种方法,推荐使用getOrElse方法。

Erlang 复制代码
//如果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)
1.11.3.3 修改可变Map信息

遍历访问map

Scala 复制代码
//修改键对应的值
//可变的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")
1.11.3.4 遍历map
Cobol 复制代码
//遍历
//返回一个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+"  ")
1.11.4 列表(List)

Scala 列表类似于数组,它们所有元素的类型都相同,但是它们也有所不同:列表是不可变的,值一旦被定义了就不能改变,其次列表是链表结构,而数组不是。

复制代码
1.列表中的元素类型必须相同。
2.列表是有序的。
3.列表是不可变的,内容及长度都不可变。
1.11.4.1 List的创建和访问
Scala 复制代码
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))
    }
}
1.11.4.2 List的遍历
Erlang 复制代码
/**
 * 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(","))
1.11.4.3 List的基本操作

对列表的所有操作可以表达为一下三种

head 返回列表的第一个元素。 tail 返回除第一个元素之外所有元素组成的列表。

isEmpty 返回列表是否为空。

注意:其中tail head作用在空列表上,会报异常。

perl 复制代码
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
1.11.4.4 可变列表ListBuffer

ListBuffer与List的区别在于:修改ListBuffer改变的是本身。

Scala 复制代码
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
}
1.11.5 Set

Set中的元素不可重复,无序(TreeSet除外)。

1.11.5.1 创建不可变集合Set
Scala 复制代码
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)
1.11.5.2 创建可变集合Set
python 复制代码
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)
}

Guff_hys_python数据结构,大数据开发学习,python实训项目-CSDN博客

相关推荐
HZ·湘怡13 分钟前
二叉树 2 堆
算法
陕西企来客4 小时前
企来客科技来客 GEO 优化系统深度解析:核心技术与原因分析
大数据·人工智能·科技·搜索引擎
wabs6667 小时前
关于贪心算法的思考
算法·贪心算法
社交怪人8 小时前
【判断大小】信息学奥赛一本通C语言解法(题号1043)
算法
J2虾虾8 小时前
Caddy在Arm64的Kylin Server上的部署
大数据·kylin
Snasph8 小时前
GNU Make 用户手册(中文版)
服务器·算法·gnu
江澎涌8 小时前
拆解与 AI 的一次对话
人工智能·算法·程序员
sheeta19989 小时前
LeetCode 每日一题笔记 日期:2026.06.02 题目:3635. 最早完成陆地和水上游乐设施的时间 II
笔记·算法·leetcode
Lsk_Smion9 小时前
力扣实训 _ [102].层序遍历--前序--后续_递归与非递归的实现
数据结构·算法·leetcode
jiayong239 小时前
Claude Code 快速参考卡片
大数据·elasticsearch·搜索引擎·ai·claude·claude code