Scala 学习 -- 其他集合类学习

Scala 学习 -- 其他集合类学习

一、序列

列表缓冲 ListBuffer

List类提供对列表头部快速访问,尾部访问并不高效。使用List类在尾部追加元素往往通过reverse,表头添加, reverse实现。

通过ListBuffer可以简单实现。ListBuffer是一个可变对象,包含在scala.collection.mutable包中。ListBuffer提供了常量的向后追加和向前追加的方法,使用+=向后追加元素,使用+=:向前追加元素。ListBuffer优于List的另一个原因是防止可能出现的栈溢出。

scala> import scala.collection.mutable.ListBuffer
import scala.collection.mutable.ListBuffer

scala> val buf = new ListBuffer[Int]
buf: scala.collection.mutable.ListBuffer[Int] = ListBuffer()

scala> buf += 1
res0: buf.type = ListBuffer(1)

scala> buf += 2
res1: buf.type = ListBuffer(1, 2)

scala> 99 +=: buf
res3: buf.type = ListBuffer(99, 1, 2)

scala> buf
res4: scala.collection.mutable.ListBuffer[Int] = ListBuffer(99, 1, 2)

scala> buf.toList
res5: List[Int] = List(99, 1, 2)

数组缓冲 ArrayBuffer

除额外从序列头部或者尾部添加移除元素外与数组基本一致。所有Array操作会稍慢,添加移除平均而言为常量时间,偶尔为线性时间,需要不时地分配新的数组来保存缓冲的内容。

scala> import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.ArrayBuffer

scala> val buf = new ArrayBuffer[Int]()
buf: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer()

scala> buf += 12
res6: buf.type = ArrayBuffer(12)

scala> buf += 15
res7: buf.type = ArrayBuffer(12, 15)

scala> buf += 18
res8: buf.type = ArrayBuffer(12, 15, 18)

scala> buf -= 12
res9: buf.type = ArrayBuffer(15, 18)

scala> buf.length
res10: Int = 2

字符串(StringOps)

Predef定义一个从String到StringOps的隐式方法,将任何字符串当作序列来处理。

二、集和映射

使用“Set”或“Map”时,默认得到一个不可变对象。可通过显示一次引入,改变为显式版本。这样的访问便利是通过Predef对象完成,每个对象内容在每个Scala源文件中隐式地引入。

object Predef{
 type Map[A,+B] = collection.immutable.Map[A,B]
 type Set[A] = collection.immutable.Set[A]
 val Map = collection.immutable.Map
 val Set = collection.immutable.Set
 //...
}

集的关键特征在于它们会确保同一时刻,以 == 为标准,集里的每个对象都最多出现一次。

//集的基本操作
//创建一个不可变集
scala> val nums = Set(1, 2, 3)
nums: scala.collection.immutable.Set[Int] = Set(1, 2, 3)

//添加一个元素
scala> nums + 5
res12: scala.collection.immutable.Set[Int] = Set(1, 2, 3, 5)

//移除一个元素
scala> nums - 3
res14: scala.collection.immutable.Set[Int] = Set(1, 2)

//添加多个元素
scala> nums ++ List(5,6)
res15: scala.collection.immutable.Set[Int] = HashSet(5, 1, 6, 2, 3)

//移除多个元素
scala> nums -- List(1,2)
res16: scala.collection.immutable.Set[Int] = Set(3)

//获取两个集合的交集
scala> nums & Set(1,3,5,7)
res17: scala.collection.immutable.Set[Int] = Set(1, 3)

//返回集的大小
scala> nums.size
res18: Int = 3

//检查是否包含
scala> nums.contains(3)
res19: Boolean = true

//让可变集易于访问
import scala.collection.mutable

//创建一个空的可变集
scala> val words = mutable.Set.empty[String]
words: scala.collection.mutable.Set[String] = HashSet()

//添加一个元素
scala> words += "the"
res20: words.type = HashSet(the)

//移除一个元素
scala> words -= "the"
res21: words.type = HashSet()

//添加多个元素
scala> words ++= List("do","re","mi")
res22: words.type = HashSet(re, do, mi)

//移除多个元素
scala> words --= List("do","re")
res23: words.type = HashSet(mi)

//清除所有元素
scala> words.clear

scala> words
res25: scala.collection.mutable.Set[String] = HashSet()

默认的集

对于可变集合,scala.collection.mutable.Set()内部使用hash表,返回scala.collection.mutable.HashSet

对于不可变集合,情况稍复杂。对于少于五个元素的集,有专门特定大小的类以此达到最好的性能。对于大于等于五个元素的集,使用哈希字典树(hash trie)实现。

元素个数 实现
0 scala.collection.immutable.EmptySet
1 scala.collection.immutable.Set1
2 scala.collection.immutable.Set2
3 scala.collection.immutable.Set3
4 scala.collection.immutable.Set4
5或更多 scala.collection.immutable.HashSet

注:

Trie树, 字典树, 哈希树变种,是一种用于快速检索的多叉树结构,应用于统计和排序大量字符串。

核心思想:以空间换时间,利用字符串公共前缀来降低查询时间开销以达到提高效率的目的。

优点:最大限度减少无谓字符串比较,查询效率较哈希表高

缺点:内存消耗非常大

三个基本特性:

  1. 根节点不包含字符,除根节点外每一个节点只包含一个字符。
  2. 从根节点到某一结点,路径上经过的字符连接起来,为该节点对应的字符串。
  3. 每个节点的所有子节点包含字符都不相同

插入过程:

​ 对于一个单词,从根开始,沿着单词的各个字母所对应的树中节点分支向下走,直至单词遍历完,将最后节点标记为红色,表示该单词已插入Trie树。

查找过程:

  1. 从根节点开始一次搜索
  2. 取得要查找的关键词的第一个字母,并根据字母选择对应子树转入建索
  3. 同上一步方法,前进一层检索
  4. 在某个节点处,关键词的所有字母都被取出,则读取附在该节点上的信息,查找完成。

排序集

Scala集合类中提供了SortedSet特质,被TreeSet类实现,实现通过红黑树来保持元素或键的顺序。具体顺序由Ordered特质决定,集的元素类型必须混入或能够被隐式转换成Ordered。

scala> import scala.collection.immutable.TreeSet
import scala.collection.immutable.TreeSet

scala> val ts = TreeSet(2,1,4,3,6,5,9,7,8)
ts: scala.collection.immutable.TreeSet[Int] = TreeSet(1, 2, 3, 4, 5, 6, 7, 8, 9)

scala> val cs = TreeSet("f","u","n")
cs: scala.collection.immutable.TreeSet[String] = TreeSet(f, n, u)

可变与不可变集选择

​ 不可变集较可变集而言,具有以下优点:

  1. 易于推敲
  2. 元素不多的情况下,不可变集合通常较可变集合存储更为紧凑

不可变集与可变集相互转换

  1. 不可变集 => 可变集 :Scala提供了一个变通的解读,只要看到a += b而a并不支持名为+=的方法,Scala将尝试将他解读为 a = a + b。【注:声明为var】

  2. 可变集 <=> 不可变集:使用empty创建一个新类型的空集合,根据集合类型,使用++或++=方法添加新元素。

集合的初始化

  1. 常见的方式:将初始元素传入所选集合的伴生对象的工厂方法
  2. 特殊方式:用别的集合初始化当前集合
scala> val colors = List("blue", "yellow", "red", "green")
colors: List[String] = List(blue, yellow, red, green)

scala> import scala.collection.immutable.TreeSet
import scala.collection.immutable.TreeSet

scala> val treeset = TreeSet(colors)
                            ^
       error: No implicit Ordering defined for List[String].

scala> val treeset = TreeSet[String]() ++ colors
treeset: scala.collection.immutable.TreeSet[String] = TreeSet(blue, green, red, yellow)

集转换成数组或列表

转换成列表或数组通常需要将集合的所有元素做拷贝,对于大型集合来说这么做可能比较费时,若集合本来元素不多,拷贝带来的性能开销并不高。

scala> treeset.toList
res1: List[String] = List(blue, green, red, yellow)

scala> treeset.toArray
res2: Array[String] = Array(blue, green, red, yellow)

映射

映射特质是对集中的每个元素关联一个值。创建映射时,必须给出两个类型。第一个类型是针对映射的键,第二个类型是针对映射的值。

//映射的常用操作
//创建一个不可变映射
scala> val nums = Map("i" -> 1, "li" -> 2)
nums: scala.collection.immutable.Map[String,Int] = Map(i -> 1, li -> 2)

//添加一个条目
scala> nums + ("vi" -> 6)
res3: scala.collection.immutable.Map[String,Int] = Map(i -> 1, li -> 2, vi -> 6)

//移除一个条目
scala> nums - "li"
res5: scala.collection.immutable.Map[String,Int] = Map(i -> 1)

//添加多个条目
scala> nums ++ List("iii" -> 3, "v" -> 5)
res6: scala.collection.immutable.Map[String,Int] = Map(i -> 1, li -> 2, iii -> 3, v -> 5)

//移除多个条目
scala> nums -- List("i","li")
res7: scala.collection.immutable.Map[String,Int] = Map()

//返回映射的大小
scala> nums.size
res8: Int = 2

//检查是否包含
scala> nums.contains("li")
res9: Boolean = true

//获取指定键的值
scala> nums("li")
res10: Int = 2

//返回所有的键
scala> nums.keys
res12: Iterable[String] = Set(i, li)

//以集的形式返回所有的键
scala> nums.keySet
res1: scala.collection.immutable.Set[String] = Set(i, li)

// 返回所有的值
scala> nums.values
res2: Iterable[Int] = View(<not computed>)

//表示映射是否为空
scala> nums.isEmpty
res3: Boolean = false

//让可变集合便于访问
import scala.collection.mutable

//创建一个空的可变映射
scala> val words = mutable.Map.empty[String,Int]
words: scala.collection.mutable.Map[String,Int] = HashMap()

//添加一个映射
scala> words += ("one" -> 1)
res4: words.type = HashMap(one -> 1)

//移除一个映射
scala> words -= "one"
res5: words.type = HashMap()

//添加多个映射
scala> words ++= List("one"->1, "two"->2, "three"->3)
res6: words.type = HashMap(one -> 1, two -> 2, three -> 3)

//移除多个映射
scala> words --= List("one", "two")
res8: words.type = HashMap(three -> 3)

默认映射

与集类似,对于可变映射而言,scala.collection.mutable.Map()工厂方法返回的是一个scala.collection.mutable.HashMap。

对于不可变映射,也与集类似。

元素个数 实现
0 scala.collection.immutable.EmptyMap
1 scala.collection.immutable.Map1
2 scala.collection.immutable.Map2
3 scala.collection.immutable.Map3
4 scala.collection.immutable.Map4
5或更多 scala.collection.immutable.HashMap

排序映射

与集类似,Scala集合类中提供了SortedMap特质,被TreeMap类实现,实现通过红黑树来保持元素或键的顺序。具体顺序由Ordered特质决定,集的元素类型必须混入或能够被隐式转换成Ordered。

scala> val treemap = TreeMap(1->"one", 3->"three", 2->"two")
treemap: scala.collection.immutable.TreeMap[Int,String] = TreeMap(1 -> one, 2 -> two, 3 -> three)

可变映射与不可变映射的相互转换

与可变集和不可变集相互转换类似,映射间转换也采用类似方法。

scala> import scala.collection.mutable
import scala.collection.mutable

scala> val muta = mutable.Map("i"->1, "ii"->2)
muta: scala.collection.mutable.Map[String,Int] = HashMap(ii -> 2, i -> 1)

scala> val immu = Map.empty ++ muta
immu: scala.collection.immutable.Map[String,Int] = Map(ii -> 2, i -> 1)

三、元组

元组

元组将一组固定个数的条目组合在一起,作为整体传递。元组可以持有不同类型的对象。常用场景为从方法返回多个值。访问元素的数组,可以用_1访问第一个元素, _2访问第二个元素。

四、计数例子

import scala.collection.mutable
object counter {
  def main(args: Array[String]): Unit = {
    val text = "See Spot run. Run, Spot, Run!"
    val ans = countWords(text)
    println(ans)
  }

  def countWords(text:String) = {
    val counts = mutable.Map.empty[String, Int]
    for (rawWord <- text.split("[ !,.]+")){
      val word = rawWord.toLowerCase
      val oldCount = {
        if (counts.contains(word))  counts(word)
        else 0
      }
      counts += (word -> (oldCount+1))
    }
    counts
  }
}
posted @ 2020-06-18 23:09  ganshuoos  阅读(182)  评论(0编辑  收藏  举报