Scala 2.8集合设计教程


73

我喘不过气来的困惑之后,有什么好的资源可以解释新的Scala 2.8集合库的结构。我很想找到一些有关以下内容的信息:

  • 集合类/性状本身(例如ListIterable
  • 为什么存在Like类(例如TraversableLike
  • 伴随方法有什么作用(例如List.companion
  • 我如何知道implicit给定点范围内的对象

如果您想要比完整的演练简短的介绍,请在最新的scalazine中找到一篇文章很柔和。一篇不错的文章还描述了图书馆背后的思想及其背后的动机。
弗朗索瓦·G

Answers:


188

前言

Martin Odersky的2.8系列演练可能是您的第一个参考。它也得到了建筑笔记的补充,这对那些想要设计自己的收藏的人们特别感兴趣。

在存在任何此类问题之前(实际上,在2.8.0本身发布之前)以其他方式编写了此答案的其余部分。

您可以找到有关此文档的文档Scala SID#3。对Scala 2.7和2.8之间的差异感兴趣的人们也应该关注该领域的其他论文。

我将选择性地引用本文的内容,并补充我的一些想法。马蒂亚斯(Matthias)在decodified.com上还生成了一些图像,可以在此处找到原始SVG文件。

收集类/特征本身

集合的特征实际上有三个层次:一个是可变集合,一个是不可变集合,一个没有对集合进行任何假设。

Scala 2.9中引入了并行,串行和也许并行集合之间的区别。我将在下一节中讨论它们。本节中描述的层次结构专门非并行集合

下图显示了Scala 2.8引入的非特定层次结构: 一般集合层次

显示的所有元素都是特征。在其他两个层次结构中,还存在直接继承特征的类,以及可以通过隐式转换为包装类而被视为属于该层次结构的类。这些图的图例可以在它们之后找到。

不可变阶层的图表: 不变的收藏体系

可变层次结构图: 可变集合层次结构

传说:

图例

对于那些看不见图像的人,这是集合层次结构的ASCII缩写形式。

                    Traversable
                         |
                         |
                      Iterable
                         |
      +------------------+--------------------+
     Map                Set                  Seq
      |                  |                    |
      |             +----+----+         +-----+------+
    Sorted Map  SortedSet   BitSet   Buffer Vector LinearSeq

平行馆藏

当Scala 2.9引入并行集合时,设计目标之一是使它们的使用尽可能无缝。用最简单的术语来说,一个人可以用一个并行的替换非并行(串行)的集合,并立即获得收益。

但是,由于在那之前所有集合都是串行的,因此许多使用它们的算法都是假设的,并取决于它们串行的。带有这种假设的方法并行收集将失败。因此,上一节中描述的所有层次结构都要求进行串行处理

创建了两个新的层次结构来支持并行集合。

并行集合层次具有的性状相同的名称,但前面有ParParIterableParSeqParMapParSet。请注意,没有ParTraversable,因为任何支持并行访问的集合都能够支持更强的ParIterable特性。它也不具有序列层次结构中存在的一些更特殊的特征。整个层次结构位于目录下scala.collection.parallel

实现并行的集合类也各不相同,有ParHashMapParHashSet两个可变和不可变的并行收集,加上ParRangeParVector实施immutable.ParSeqParArray执行mutable.ParSeq

另一个层次还存在镜子串行和并行集合的特点,但有一个前缀GenGenTraversableGenIterableGenSeqGenMapGenSet。这些特征是父母双方并行和串行集合。这意味着采用a的方法Seq不能接收并行集合,但是采用a的方法GenSeq可以用于串行和并行集合。

考虑到这些层次结构的构造方式,为Scala 2.8编写的代码与Scala 2.9完全兼容,并要求串行行为。如果不进行重写,则无法利用并行集合,但是所需的更改很小。

使用并行集合

通过调用任何方法都可以将其转换为并行方法par。同样,任何集合都可以通过调用其seq上的方法转换为串行集合。

如果集合已经是请求的类型(并行或串行),则不会进行任何转换。但是,如果调用seq并行集合或par串行集合,则会生成具有所请求特征的新集合。

请勿将seq,与toSeqSeq从集合的元素返回创建的,与将一个集合变成非并行集合的混淆。调用toSeq并行集合将返回ParSeq,而不是串行集合。

主要特征

尽管有许多实现类和子特性,但层次结构中还是有一些基本特性,每个特性都提供了更多的方法或更具体的保证,但减少了可以实现它们的类的数量。

在以下小节中,我将简要描述主要特征及其背后的思想。

特质遍历

该特征Traversable与下面描述的特征非常相似,但是有一个限制,即您只能使用一次。也就是说,在a上调用的任何方法都TraversableOnce 可能使其无法使用。

此限制使得在和之间可以共享相同的方法Iterator。这使得Iterator使用而不使用Iterator特定方法的方法实际上可以完全使用任何集合以及迭代器(如果重写为accept)TraversableOnce

因为TraversableOnce集合和迭代器是统一的,所以它不会出现在先前的图中,后者仅与集合有关。

特质可穿越

集合层次结构的顶部是trait Traversable。它唯一的抽象操作是

def foreach[U](f: Elem => U)

该操作旨在遍历集合的所有元素,并将给定的操作f应用于每个元素。该应用仅出于其副作用而完成;实际上,f的任何函数结果都会被foreach丢弃。

可穿越的对象可以是有限的或无限的。无限可遍历对象的一个​​示例是自然数流Stream.from(0)。该方法hasDefiniteSize指示集合是否可能是无限的。如果hasDefiniteSize返回true,则集合肯定是有限的。如果返回false,则说明集合尚未完全详细说明,因此它可能是无限的或有限的。

此类定义了可以有效地实现foreach(超过40种)的方法。

特质可迭代

此特征声明一个抽象方法iterator,该方法返回一个迭代器,该迭代器逐个生成集合的所有元素。中的foreach方法根据Iterable实现iteratorIterable通常,子类通常使用直接实现来覆盖foreach以提高效率。

Iterable还向中添加了一些不常用的方法Traversable,只有当iterator可用时才能有效地实现。它们总结如下。

xs.iterator          An iterator that yields every element in xs, in the same order as foreach traverses elements.
xs takeRight n       A collection consisting of the last n elements of xs (or, some arbitrary n elements, if no order is defined).
xs dropRight n       The rest of the collection except xs takeRight n.
xs sameElements ys   A test whether xs and ys contain the same elements in the same order

其他特点

Iterable有来了三个基本的特征,其继承它:SeqSet,和Map。这三个都具有apply方法,并且三个都实现了PartialFunction特征,但是apply每种情况下的含义都不同。

我相信的含义SeqSet而且Map很直观。在它们之后,这些类在特定的实现中分解,这些实现为性能及其所提供的方法提供了特别的保证。此外,还包括一些特质与进一步细化,如LinearSeqIndexedSeqSortedSet

下面的清单可能会得到改善。发表评论并提出建议,我会解决。

基类和特质

  • Traversable-基本的收藏课。可以用实现foreach
    • TraversableProxy-代理Traversable。只是指向self真正的收藏。
    • TraversableView -具有某些非严格方法的Traversable。
    • TraversableForwarder-前锋大多数方法underlying,除了toStringhashCodeequalsstringPrefixnewBuilderview和所有调用创建相同类型的新的迭代对象。
    • mutable.Traversableimmutable.Traversable-同样的事情Traversable,但限制集合类型。
    • 存在其他特殊情况的Iterable类,例如MetaData
    • Iterable-Iterator可以为其创建的集合(通过iterator)。
      • IterableProxyIterableViewmutableimmutable.Iterable
  • Iterator-不是的后代的特征Traversable。定义nexthasNext
    • CountedIterator-Iterator定义count,返回到目前为止已看到的元素。
    • BufferedIterator-Defines head,它返回下一个元素而不消耗它。
    • 存在其他特殊情况的Iterator类,例如Source

地图

  • Map-一个IterableTuple2,这也提供了用于检索给定的密钥(该元组的第一个元素)的值(该元组的第二个元素)的方法。也延伸PartialFunction
    • MapProxy-AProxy代表Map
    • DefaultMap-一种实现某些Map抽象方法的特征。
    • SortedMap-Map按键已排序的。
      • immutable.SortMap
        • immutable.TreeMap-一个实现类immutable.SortedMap
    • immutable.Map
      • immutable.MapProxy
      • immutable.HashMap-immutable.Map通过密钥散列实现的类。
      • immutable.IntMap-实现immutable.Map专用于Int密钥的类。使用基于键的二进制数字的树。
      • immutable.ListMap-immutable.Map通过列表实现的类。
      • immutable.LongMap-实现immutable.Map专用于Long密钥的类。请参阅IntMap
      • 还有其他针对特定数量元素优化的类。
    • mutable.Map
      • mutable.HashMap-mutable.Map通过密钥散列实现的类。
      • mutable.ImmutableMapAdaptor-mutable.Map从现有的类实现a的类immutable.Map
      • mutable.LinkedHashMap -?
      • mutable.ListMap-mutable.Map通过列表实现的类。
      • mutable.MultiMap -一个类,每个键接受多个不同的值。
      • mutable.ObservableMap-甲混入其中,当与混合Map,通过发布事件到观察者Publisher接口。
      • mutable.OpenHashMap -基于开放式哈希算法的类。
      • mutable.SynchronizedMap-一个mixin,应与混合使用,Map以提供带有同步方法的版本。
      • mutable.MapProxy

序列

  • Seq-一系列元素。假定大小和元素重复定义明确。也延伸PartialFunction
    • IndexedSeq -支持O(1)元素访问和O(1)长度计算的序列。
      • IndexedSeqView
      • immutable.PagedSeq-IndexedSeq通过传递给构造函数的函数按需生成元素的实现。
      • immutable.IndexedSeq
        • immutable.Range -分隔的整数序列,在低端封闭,在高端开放,并带有阶跃。
          • immutable.Range.Inclusive-Range高端也是如此。
          • immutable.Range.ByOne-Range步骤为1。
        • immutable.NumericRange-通用的版本Range适用于任何版本Integral
          • immutable.NumericRange.Inclusiveimmutable.NumericRange.Exclusive
          • immutable.WrappedStringimmutable.RichString-包装器,可将aString视为Seq[Char],同时仍保留String方法。我不确定它们之间有什么区别。
      • mutable.IndexedSeq
        • mutable.GenericArray-Seq基于数组的结构。请注意,“类”Array是Java的Array,它比类更多地是一种内存存储方法。
        • mutable.ResizableArray -基于可调整大小的数组的类使用的内部类。
        • mutable.PriorityQueuemutable.SynchronizedPriorityQueue-实现优先队列的类-根据Ordering第一个队列和最后一个队列的顺序对元素进行出队列的队列。
        • mutable.PriorityQueueProxy-一个抽象ProxyPriorityQueue
    • LinearSeq-线性序列A的性状,具有高效的时间isEmptyheadtail
      • immutable.LinearSeq
        • immutable.List -不变的单链接列表实现。
        • immutable.Stream-懒惰列表。它的元素仅按需计算,但随后会被记忆(保留在内存中)。理论上它可以是无限的。
      • mutable.LinearSeq
        • mutable.DoublyLinkedList-具有可变的列表prevheadelem)和tailnext)。
        • mutable.LinkedList-带有可变headelem)和tailnext)的列表。
        • mutable.MutableList -在内部用于基于可变列表实现类的类。
          • mutable.Queuemutable.QueueProxy-为FIFO(先进先出)操作优化的数据结构。
          • mutable.QueueProxy-AProxy代表mutable.Queue
    • SeqProxySeqViewSeqForwarder
    • immutable.Seq
      • immutable.Queue-实现FIFO优化(先进先出)数据结构的类。mutableimmutable队列没有通用的超类。
      • immutable.Stack-实现LIFO优化(后进先出)数据结构的类。两者没有共同的超类mutable immutable堆栈。
      • immutable.Vector -?
      • scala.xml.NodeSeq–扩展的专用XML类immutable.Seq
      • immutable.IndexedSeq -如上所示。
      • immutable.LinearSeq -如上所示。
    • mutable.ArrayStack-使用数组实现LIFO优化数据结构的类。据说比普通堆栈快得多。
    • mutable.Stackmutable.SynchronizedStack-实现LIFO优化的数据结构的类。
    • mutable.StackProxy-一个Proxymutable.Stack..
    • mutable.Seq
      • mutable.Buffer -元素的顺序,可以通过追加,添加或插入新成员来更改。
        • mutable.ArrayBuffer-mutable.Buffer类的实现,具有用于追加,更新和随机访问操作的固定摊销时间。它具有一些专门的子类,例如NodeBuffer
        • mutable.BufferProxymutable.SynchronizedBuffer
        • mutable.ListBuffer-由列表支持的缓冲区。它提供固定的时间追加和前置,其他大多数操作都是线性的。
        • mutable.ObservableBuffer-混合特性,当与混合时Buffer,可通过Publisher接口提供通知事件。
        • mutable.IndexedSeq -如上所示。
        • mutable.LinearSeq -如上所示。

集合

  • Set -集合是包含最多任何对象的集合。
    • BitSet -一组存储为位集的整数。
      • immutable.BitSet
      • mutable.BitSet
    • SortedSet -元素排序的集合。
      • immutable.SortedSet
        • immutable.TreeSet-SortedSet基于树的实现。
    • SetProxy-AProxy代表Set
    • immutable.Set
      • immutable.HashSet-Set基于元素哈希的实现。
      • immutable.ListSet-Set基于列表的实现。
      • 存在其他集合类,以为0到4个元素的集合提供优化的实现。
      • immutable.SetProxy-AProxy为一成不变Set
    • mutable.Set
      • mutable.HashSet-Set基于元素哈希的实现。
      • mutable.ImmutableSetAdaptor-Set从不可变实现可变的类Set
      • LinkedHashSet-Set基于列表的实现。
      • ObservableSet-混合特征,当与混合时Set,可通过Publisher接口提供通知事件。
      • SetProxy-AProxy代表Set
      • SynchronizedSet-混合特征,当与混合时Set,可通过Publisher接口提供通知事件。

  • 为什么存在Like类(例如TraversableLike)

这样做是为了实现最大程度的代码重用。具有特定结构(可遍历,映射等)的类的具体通用实现在Like类中完成。然后,用于一般消费的类将覆盖可以优化的所选方法。

  • 伴随方法有什么用(例如List.companion)

类的生成器,即知道如何以类似方法使用的方式创建该类实例的对象 map,是由伴随对象中的方法创建的。因此,为了构建X类型的对象,我需要从X的伴随对象中获得该生成器。不幸的是,在Scala中,无法从X类到X对象。在X的每个实例中定义的方法companion,该方法返回类X的伴随对象。

尽管这种方法在普通程序中可能会有一些用途,但其目标是在集合库中实现代码重用。

  • 我如何知道给定点范围内的隐式对象

您不应该在乎这一点。它们是精确隐式的,因此您无需弄清楚如何使其工作。

这些隐式存在是为了使可以在父类上定义集合上的方法,但仍返回相同类型的集合。例如,map方法是在上定义的TraversableLike,但是如果在上使用,List则会得到List回报。


将Option添加到图中,作为一个孤独的孤儿出现在角落是否有意义?我知道这不是真正的收藏-更多是想收藏的-但它可能会帮助像我这样的白痴
Ed Staub'3

1
@EdStaub我不想。它们都是容器,是的,并且,与任何容器一样,它们都是单子。但是,除此之外,它们之间并没有太多共同之处。
Daniel C. Sobral

@Guillaume也可以在docs.scala-lang.org上获得,在此处可以保持最新状态。
Daniel C. Sobral 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.