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树, 字典树, 哈希树变种,是一种用于快速检索的多叉树结构,应用于统计和排序大量字符串。
核心思想:以空间换时间,利用字符串公共前缀来降低查询时间开销以达到提高效率的目的。
优点:最大限度减少无谓字符串比较,查询效率较哈希表高
缺点:内存消耗非常大
三个基本特性:
- 根节点不包含字符,除根节点外每一个节点只包含一个字符。
- 从根节点到某一结点,路径上经过的字符连接起来,为该节点对应的字符串。
- 每个节点的所有子节点包含字符都不相同
插入过程:
对于一个单词,从根开始,沿着单词的各个字母所对应的树中节点分支向下走,直至单词遍历完,将最后节点标记为红色,表示该单词已插入Trie树。
查找过程:
- 从根节点开始一次搜索
- 取得要查找的关键词的第一个字母,并根据字母选择对应子树转入建索
- 同上一步方法,前进一层检索
- 在某个节点处,关键词的所有字母都被取出,则读取附在该节点上的信息,查找完成。
排序集
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)
可变与不可变集选择
不可变集较可变集而言,具有以下优点:
- 易于推敲
- 元素不多的情况下,不可变集合通常较可变集合存储更为紧凑
不可变集与可变集相互转换
不可变集 => 可变集 :Scala提供了一个变通的解读,只要看到a += b而a并不支持名为+=的方法,Scala将尝试将他解读为 a = a + b。【注:声明为var】
可变集 <=> 不可变集:使用empty创建一个新类型的空集合,根据集合类型,使用++或++=方法添加新元素。
集合的初始化
- 常见的方式:将初始元素传入所选集合的伴生对象的工厂方法
- 特殊方式:用别的集合初始化当前集合
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 } }