过时通知
您现在已经看到了 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.toList
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
链表
链表是可变序列,由通过下一个指针链接的节点组成。它们由类LinkedList支持。在大多数语言中,null
会被选作空链表。这对于 Scala 集合不起作用,因为即使是空序列也必须支持所有序列方法。特别是LinkedList.empty.isEmpty
应该返回true
,而不是抛出NullPointerException
。空链表以特殊方式编码:它们的next
字段指向节点本身。与它们不可变的表亲一样,最好顺序遍历链表。此外,链表可以轻松地将元素或链表插入另一个链表中。
双向链表
双向链表与单向链表类似,除了它们除了next
之外,还有另一个可变字段prev
,指向当前节点前面的元素。该附加链接的主要好处是它使元素删除非常快。双向链表由类DoubleLinkedList支持。
可变列表
MutableList由一个单向链表和一个指向该列表的终端空节点的指针组成。这使得列表追加成为一个常量时间操作,因为它避免了遍历列表以搜索其终端节点。MutableList目前是 Scala 中mutable.LinearSeq的标准实现。
队列
除了不可变队列,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)
数组序列
数组序列是固定大小的可变序列,它们在内部将元素存储在 Array[Object]
中。它们在 Scala 中由类 ArraySeq 实现。
如果您想要一个具有性能特征的数组,但您还想要创建序列的泛型实例(您不知道元素的类型,并且没有 ClassTag
在运行时提供它),那么您通常会使用 ArraySeq
。这些问题在 数组 部分中进行了说明。
堆栈
您之前看到了不可变堆栈。还有一个可变版本,由类 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)
数组堆栈
ArrayStack 是可变堆栈的替代实现,它由一个根据需要重新调整大小的数组支持。它提供快速索引,并且通常比普通可变堆栈对大多数操作的效率稍高。
哈希表
哈希表将其元素存储在底层数组中,将每个项目放置在由该项目的哈希码确定的数组中的位置。将元素添加到哈希表中只需恒定时间,只要数组中没有另一个具有相同哈希码的元素即可。因此,只要放置在其中的对象具有良好的哈希码分布,哈希表就会非常快。因此,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 操作之外,它还提供以下原子操作
ConcurrentMap 类中的操作
它是什么 | 它做什么 |
---|---|
m putIfAbsent(k, v) |
添加键/值绑定 k -> v ,除非 k 已在 m 中定义 |
m remove (k, v) |
如果当前将条目 k 映射到 v ,则删除该条目。 |
m replace (k, old, new) |
如果键 k 之前绑定到 old ,则将与键 k 关联的值替换为 new 。 |
m replace (k, v) |
如果键 k 之前绑定到某个值,则将与键 k 关联的值替换为 v 。 |
ConcurrentMap
是 Scala 集合库中的一个特征。当前,其唯一实现是 Java 的 java.util.concurrent.ConcurrentMap
,可以使用 标准 Java/Scala 集合转换 将其自动转换为 Scala 映射。
可变位集
可变位集 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)