scala学习笔记11:集合

45 阅读6分钟

scala学习笔记


@TOC


前言

Scala的提供的集合有三大类: 序列Seq、集Set、映射Map。 这些三类又衍生出很多子类,这些集合的特点一时很难全部了解和掌握,我们只能从一些常用的开始,在日后的工作中如果需要再去深入了解。 每个集合类型都包含了大量方法,这些方法能让我们的工作更轻松,但同样我们也不比记住每一个方法,可以先从一些常用的方法开始,去解决实际的问题,循序渐进。


一、不可变集合&&可变集合

所有的集合都扩展自Iterable特质,在Scala中集合都有可变(mutable)和不可变(immutable)两种类型。对应两个重要的包: 不可变集合:scala.collection.immutable 可变集合: scala.collection.mutable 1.scala不可变集合,就是这个 集合本身不能动态变化。长度一旦确定就没法修改,增删元素只能把结果赋给新的一个集合,集合本身不会发生改变,但可以修改里面的元素的值。 2.可变集合就是这个 集合本身可以动态变化的,增加或删减都可以基于这个集合本身操作。

scala集合继承关系图,这些都是高级的抽象类或者特质 在这里插入图片描述

不可变集合继承结构图: 在这里插入图片描述

可以看到Set,Map,Seq全部扩展自Iterable特质. Set集合有包含了HashSet,SortedSet(又包含TreeSet),BitSet,ListSet Map集合有包含了HashMap,SortedMap(又包含TreeMap),ListMap Seq集合有主要分为indexedSeq(索引序列)和linearSeq(线性序列),地下又有所细分。

可变结合继承结构图 在这里插入图片描述

整体结构差不多,但是内容更丰富,序列中多了Buffer,常用的如ArrayBuffer和ListBuffer。涉及线程安全的用syn开头的结构。 总结et、Map是Java中也有的集合

  • Seq是Java没有的,List归属到Seq了,所以这里的List就和java不是同一个概念了
  • String也是属于IndexeSeq
  • 我们发现经典的数据结构比如Queue 和 Stack被归属到LinearSeq
  • Scala中的Map体系有一个SortedMap,说明Scala的Map可以支持 排序
  • IndexSeq 和 LinearSeq 的区别[IndexSeq是通过索引来查找和定位, 因此速度快,比如String就是一个索引集合,通过索引即可定位][LineaSeq 是线型的,即有头尾的概念,这种数据结构一般是通过遍历来查找。

二、Sequences(序列)

Seq序列是元素的顺序集合,可以是索引的(如数组)或线性的(如链表)。 当我们选择一个序列时,需要决定两点 1.序列应该被索引(像数组一样) ,允许快速访问任何元素,还是应该实现为线性链表。 2.是需要一个可变的还是不可变的集合?

项目不可变可变
索引Vector、ArrayArrayBuffer
线性ListListBuffer

List

1.创建一个list

val ints = List(1, 2, 3)
//结果:List(1, 2, 3)

val names = List("Joel", "Chris", "Ed")
//结果:List(Joel, Chris, Ed)

// another way to construct a List
val namesAgain = "Joel" :: "Chris" :: "Ed" :: Nil
//结果:List(Joel, Chris, Ed)

可以在创建指定类型

//List[Int]限定元素只能为Int
val ints: List[Int] = List(1, 2, 3)
//List[String]限定元素只能为String
val names: List[String] = List("Joel", "Chris", "Ed")
//混合类型,这时候最好指定类型
val things: List[Any] = List("Joel", "Chris", "Ed",1,5.0)

2.增:

//单元素添加 ::
val new_name=   "james" :: names
//结果:List(james, Joel, Chris, Ed)
//val new_name3=  names :: "james" 这种添加是错误的。只能往前加,因为是List是单链表

//两个List合并,三冒号  :::
val new_name2=   List("james","lbj") ::: names
//结果:List(james, lbj, Joel, Chris, Ed)

+:& :+ 看着有点乱,有个办法方便记忆,冒号:代表序列所在的那一边

val names = List("Joel", "Chris", "Ed")
val new_name3= "polo" +: names
val new_name4= names :+ "polo"
println(new_name3)
//List(polo, Joel, Chris, Ed)
println(new_name4)
//List(Joel, Chris, Ed, polo)

查:

val names2= "Joel" :: "Chris" :: "Ed" :: Nil
 println(names2(2))
 //结果:Ed

因为 List 是一个链表,所以不应该尝试通过索引值访问大型列表的元素。例如,如果 List 中有100万个元素,访问 myList (999 _ 999)这样的元素将花费相对较长的时间,因为该请求必须遍历所有这些元素。如果您有一个大型集合,并且希望通过索引访问元素,那么可以使用 Vector 或 ArrayBuffer。

遍历: 使用 for 循环的一个好处是 Scala 是一致的,同样的方法适用于所有序列,包括 Array、 ArrayBuffer、 List、 Seq、 Vector、 Map、 Set 等。

 for(name <- names)
    println(name)

2.Vector

Vector 是一个有索引的、不可变的序列。“索引”意味着它提供了的随机访问和更新,不需要遍历整个列表,因此可以通过它们的索引值快速访问 Vector 元素. 1.创建

val nums = Vector(1, 2, 3, 4, 5)

val strings = Vector("one", "two")

case class Person(name: String)
val people = Vector(
  Person("Bert"),
  Person("Ernie"),
  Person("Grover")
)

2.增 不可变,所以不能再原来的vector增加,只能创建一个新得

//末尾追加
val a = Vector(1,2,3)         // Vector(1, 2, 3)
val b = a :+ 4                // Vector(1, 2, 3, 4)
val c = a ++ Vector(4, 5)     // Vector(1, 2, 3, 4, 5)
//队前追加
val a = Vector(1,2,3)         // Vector(1, 2, 3)
val b = 0 +: a                // Vector(0, 1, 2, 3)
val c = Vector(-1, 0) ++: a   // Vector(-1, 0, 1, 2, 3)

3.ArrayBuffer

常用的可变seq有ArrayBuffer,ListBuffer 重点介绍一下ArrayBuffer。 1.创建

//导入包
import scala.collection.mutable.ArrayBuffer

//带类型的ArrayBuffer
var strings = ArrayBuffer[String]()
var ints = ArrayBuffer[Int]()
var people = ArrayBuffer[Person]()

//指定初始长度核类型
// ready to hold 100,000 ints
val buf = new ArrayBuffer[Int](100_000)
//初始元素
val nums = ArrayBuffer(1, 2, 3)
val people = ArrayBuffer(
  Person("Bert"),
  Person("Ernie"),
  Person("Grover")
)

2.增加元素: 符号函数:+=&++= 名称函数:队尾(append, appendAll),队中( insert, insertAll), 队头(prepend, and prependAll)

val nums = ArrayBuffer(1, 2, 3)   // ArrayBuffer(1, 2, 3)
nums += 4                         // ArrayBuffer(1, 2, 3, 4)
nums ++= List(5, 6)               // ArrayBuffer(1, 2, 3, 4, 5, 6)

3.删 符号函数:-=&--= 名称函数:clear, remove

val a = ArrayBuffer.range('a', 'h')   // ArrayBuffer(a, b, c, d, e, f, g)
a -= 'a'                              // ArrayBuffer(b, c, d, e, f, g)
a --= Seq('b', 'c')                   // ArrayBuffer(d, e, f, g)
a --= Set('d', 'e')                   // ArrayBuffer(f, g)

4.改

val a = ArrayBuffer.range(1,5)        // ArrayBuffer(1, 2, 3, 4)
a(2) = 50                             // ArrayBuffer(1, 2, 50, 4)
a.update(0, 10)                       // ArrayBuffer(10, 2, 50, 4)

三、Maps

映射包含一组键/值对,如 Java Map、 Python 的字典dictionary 。同样在scala里也有可变和不可变的区分。 1.不可变Map

//创建
val states = Map(
  "AK" -> "Alaska",
  "AL" -> "Alabama",
  "AZ" -> "Arizona"
)

查询
 keys, keySet, keysIterator, for
```scala
//通过Key,查询value
val ak = states("AK")   // ak: String = Alaska
val al = states("AL")   // al: String = Alabama

增加: ++&+

val a = Map(1 -> "one")    // a: Map(1 -> one)
val b = a + (2 -> "two")   // b: Map(1 -> one, 2 -> two)
val c = b ++ Seq(
  3 -> "three",
  4 -> "four"
)
// c: Map(1 -> one, 2 -> two, 3 -> three, 4 -> four)

删除: --&-

val a = Map(
  1 -> "one",
  2 -> "two",
  3 -> "three",
  4 -> "four"
)
val b = a - 4       // b: Map(1 -> one, 2 -> two, 3 -> three)
val c = a - 4 - 3   // c: Map(1 -> one, 2 -> two)

改: updated or +

val a = Map(
  1 -> "one",
  2 -> "two",
  3 -> "three"
)
val b = a.updated(3, "THREE!")   // b: Map(1 -> one, 2 -> two, 3 -> THREE!)
val c = a + (2 -> "TWO...")      // c: Map(1 -> one, 2 -> TWO..., 3 -> three)

//遍历

for (k, v) <- states do println(s"key: $k, value: $v")
//key: AK, value: Alaska
//key: AL, value: Alabama
//key: AZ, value: Arizona

有许多方法可以处理映射中的键和值。常见的 Map 方法包括 foreach、 Map、 key 和 value。 Scala 有许多专门的 Map 类型,包括 CollisionProofHashMap、 HashMap、 LinkedHashMap、 ListMap、 SortedMap、 TreeMap、 WeakHashMap 等。

四、Sets

Scala 集是一个没有重复元素的可迭代集合。 1.不可变set

//创建
val nums = Set[Int]()
val letters = Set[Char]()
//带初始值创建
val nums = Set(1, 2, 3, 3, 3)           // Set(1, 2, 3)
val letters = Set('a', 'b', 'c', 'c')   // Set('a', 'b', 'c')

增加: +&++

val a = Set(1, 2)                // Set(1, 2)
val b = a + 3                    // Set(1, 2, 3)
val c = b ++ Seq(4, 1, 5, 5)     // HashSet(5, 1, 2, 3, 4)

删除 -&--

val a = Set(1, 2, 3, 4, 5)   // HashSet(5, 1, 2, 3, 4)
val b = a - 5                // HashSet(1, 2, 3, 4)
val c = b -- Seq(3, 4)       // HashSet(1, 2)

5.Range

Scala Range 通常用于填充数据结构,生成测试数据和迭代 for 循环。这些 REPL 示例演示了如何创建范围。 几种创建方式

1 to 5         // Range(1, 2, 3, 4, 5)
1 until 5      // Range(1, 2, 3, 4)
1 to 10 by 2   // Range(1, 3, 5, 7, 9)
'a' to 'c'     // NumericRange(a, b, c)

val x = (1 to 5).toList     // List(1, 2, 3, 4, 5)
val x = (1 to 5).toBuffer   // ArrayBuffer(1, 2, 3, 4, 5)

Vector.range(1, 5)       // Vector(1, 2, 3, 4)
List.range(1, 10, 2)     // List(1, 3, 5, 7, 9)
Set.range(1, 10)         // HashSet(5, 1, 6, 9, 2, 7, 3, 8, 4)

val evens = (0 to 10 by 2).toList     // List(0, 2, 4, 6, 8, 10)
val odds = (1 to 10 by 2).toList      // List(1, 3, 5, 7, 9)
val doubles = (1 to 5).map(_ * 2.0)   // Vector(2.0, 4.0, 6.0, 8.0, 10.0)