你现在已经看到了 Scala 在其标准库中提供的最常用的不可变集合类。现在来看看可变集合类。
数组缓冲区
一个 ArrayBuffer 缓冲区包含一个数组和一个大小。对数组缓冲区的大多数操作都与对数组的速度相同,因为这些操作只是访问和修改底层数组。此外,可以有效地将数据添加到数组缓冲区的末尾。将一个项目附加到数组缓冲区需要摊销常数时间。因此,每当新项目总是添加到末尾时,数组缓冲区对于有效地建立大型集合非常有用。
scala> val buf = scala.collection.mutable.ArrayBuffer.empty[Int]
buf: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer()
scala> buf += 1
res32: buf.type = ArrayBuffer(1)
scala> buf += 10
res33: buf.type = ArrayBuffer(1, 10)
scala> buf.toArray
res34: Array[Int] = Array(1, 10)
列表缓冲区
一个 ListBuffer 就像一个数组缓冲区,只不过它在内部使用链表而不是数组。如果你计划在建立缓冲区后将其转换为列表,请使用列表缓冲区而不是数组缓冲区。
scala> val buf = scala.collection.mutable.ListBuffer.empty[Int]
buf: scala.collection.mutable.ListBuffer[Int] = ListBuffer()
scala> buf += 1
res35: buf.type = ListBuffer(1)
scala> buf += 10
res36: buf.type = ListBuffer(1, 10)
scala> buf.to(List)
res37: List[Int] = List(1, 10)
字符串生成器
就像数组缓冲区对于构建数组很有用,列表缓冲区对于构建列表很有用,一个 StringBuilder 对于构建字符串很有用。字符串生成器非常常用,以至于它们已经导入到默认名称空间中。使用简单的 new StringBuilder
创建它们,如下所示
scala> val buf = new StringBuilder
buf: StringBuilder =
scala> buf += 'a'
res38: buf.type = a
scala> buf ++= "bcdef"
res39: buf.type = abcdef
scala> buf.toString
res41: String = abcdef
scala> val buf = StringBuilder()
buf: StringBuilder =
scala> buf += 'a'
res38: buf.type = a
scala> buf ++= "bcdef"
res39: buf.type = abcdef
scala> buf.toString
res41: String = abcdef
ArrayDeque
一个 ArrayDeque 是一个序列,支持在前面和后面有效地添加元素。它在内部使用可调整大小的数组。
如果你需要向缓冲区追加和前置元素,请使用 ArrayDeque
而不是 ArrayBuffer
。
队列
除了不可变队列之外,Scala 还提供了可变队列。使用 mQueue
的方式与使用不可变队列的方式类似,但使用 +=
和 ++=
运算符进行追加,而不是使用 enqueue
。此外,在可变队列上,dequeue
方法只会从队列中移除头元素并返回该元素。下面是一个示例
scala> val queue = new scala.collection.mutable.Queue[String]
queue: scala.collection.mutable.Queue[String] = Queue()
scala> queue += "a"
res10: queue.type = Queue(a)
scala> queue ++= List("b", "c")
res11: queue.type = Queue(a, b, c)
scala> queue
res12: scala.collection.mutable.Queue[String] = Queue(a, b, c)
scala> queue.dequeue
res13: String = a
scala> queue
res14: scala.collection.mutable.Queue[String] = Queue(b, c)
scala> val queue = scala.collection.mutable.Queue[String]()
queue: scala.collection.mutable.Queue[String] = Queue()
scala> queue += "a"
res10: queue.type = Queue(a)
scala> queue ++= List("b", "c")
res11: queue.type = Queue(a, b, c)
scala> queue
res12: scala.collection.mutable.Queue[String] = Queue(a, b, c)
scala> queue.dequeue
res13: String = a
scala> queue
res14: scala.collection.mutable.Queue[String] = Queue(b, c)
栈
栈实现了一种数据结构,允许以后进先出 (LIFO) 的方式存储和检索对象。它受 mutable.Stack 类支持。
scala> val stack = new scala.collection.mutable.Stack[Int]
stack: scala.collection.mutable.Stack[Int] = Stack()
scala> stack.push(1)
res0: stack.type = Stack(1)
scala> stack
res1: scala.collection.mutable.Stack[Int] = Stack(1)
scala> stack.push(2)
res0: stack.type = Stack(1, 2)
scala> stack
res3: scala.collection.mutable.Stack[Int] = Stack(1, 2)
scala> stack.top
res8: Int = 2
scala> stack
res9: scala.collection.mutable.Stack[Int] = Stack(1, 2)
scala> stack.pop
res10: Int = 2
scala> stack
res11: scala.collection.mutable.Stack[Int] = Stack(1)
scala> val stack = scala.collection.mutable.Stack[Int]()
stack: scala.collection.mutable.Stack[Int] = Stack()
scala> stack.push(1)
res0: stack.type = Stack(1)
scala> stack
res1: scala.collection.mutable.Stack[Int] = Stack(1)
scala> stack.push(2)
res0: stack.type = Stack(1, 2)
scala> stack
res3: scala.collection.mutable.Stack[Int] = Stack(1, 2)
scala> stack.top
res8: Int = 2
scala> stack
res9: scala.collection.mutable.Stack[Int] = Stack(1, 2)
scala> stack.pop
res10: Int = 2
scala> stack
res11: scala.collection.mutable.Stack[Int] = Stack(1)
可变 ArraySeq
数组序列是固定大小的可变序列,它们在内部将元素存储在 Array[Object]
中。它们在 Scala 中由 ArraySeq 类实现。
如果你想要一个具有性能特征的数组,但你又想创建序列的泛型实例(其中你不知道元素的类型,也没有 ClassTag
在运行时提供它),则通常会使用 ArraySeq
。这些问题在 数组 一节中进行了说明。
哈希表
哈希表将元素存储在底层数组中,将每个项目放置在由该项目的哈希码确定的数组位置。将元素添加到哈希表仅需恒定时间,只要数组中没有其他元素具有相同的哈希码即可。因此,只要放置在哈希表中的对象的哈希码分布良好,哈希表就会非常快。因此,Scala 中的默认可变映射和集合类型基于哈希表。你还可以直接通过名称 mutable.HashSet 和 mutable.HashMap 访问它们。
哈希集合和映射的使用方式与任何其他集合或映射相同。以下是一些简单的示例
scala> val map = scala.collection.mutable.HashMap.empty[Int,String]
map: scala.collection.mutable.HashMap[Int,String] = Map()
scala> map += (1 -> "make a web site")
res42: map.type = Map(1 -> make a web site)
scala> map += (3 -> "profit!")
res43: map.type = Map(1 -> make a web site, 3 -> profit!)
scala> map(1)
res44: String = make a web site
scala> map contains 2
res46: Boolean = false
不能保证按特定顺序迭代哈希表。迭代只是按底层数组的顺序进行,无论其顺序如何。若要获得有保证的迭代顺序,请使用链接哈希映射或集合,而不是常规哈希映射或集合。链接哈希映射或集合与常规哈希映射或集合类似,只是它还包括一个按添加顺序排列的元素链表。对这种集合的迭代始终按元素最初添加的顺序进行。
弱哈希映射
弱哈希映射是一种特殊类型的哈希映射,其中垃圾收集器不会遵循从映射到其中存储的键的链接。这意味着如果不存在对该键的其他引用,则键及其关联的值将从映射中消失。弱哈希映射对于缓存等任务很有用,在这些任务中,如果在同一键上再次调用函数,则希望重新使用昂贵的函数结果。如果键和函数结果存储在常规哈希映射中,则映射可能会无限增长,并且永远不会有键变成垃圾。使用弱哈希映射可以避免此问题。一旦键对象变得不可达,其条目就会从弱哈希映射中删除。Scala 中的弱哈希映射由类 WeakHashMap 实现,作为底层 Java 实现 java.util.WeakHashMap
的包装器。
并发映射
并发映射可由多个线程同时访问。除了通常的 Map 操作外,它还提供以下原子操作
类 concurrent.Map 中的操作
它是什么 | 它做什么 |
---|---|
m.putIfAbsent(k, v) |
添加键/值绑定 k -> v ,除非 k 已在 m 中定义 |
m.remove(k, v) |
如果条目当前映射到 v ,则删除 k 的条目。 |
m.replace(k, old, new) |
将与键 k 关联的值替换为 new ,如果它以前绑定到 old 。 |
m.replace (k, v) |
将与键 k 关联的值替换为 v ,如果它以前绑定到某个值。 |
concurrent.Map
是 Scala 集合库中的一个特征。目前,它有两个实现。第一个是 Java 的 java.util.concurrent.ConcurrentMap
,可以使用 标准 Java/Scala 集合转换 自动将其转换为 Scala 映射。第二个实现是 TrieMap,它是哈希数组映射树的无锁实现。
可变位集
类型为 mutable.BitSet 的可变位集就像不可变位集一样,不同之处在于它在原地修改。可变位集在更新时比不可变位集效率稍高,因为它们不必复制未更改的 Long
。
scala> val bits = scala.collection.mutable.BitSet.empty
bits: scala.collection.mutable.BitSet = BitSet()
scala> bits += 1
res49: bits.type = BitSet(1)
scala> bits += 3
res50: bits.type = BitSet(1, 3)
scala> bits
res51: scala.collection.mutable.BitSet = BitSet(1, 3)