编写Scala代码时,我经常遇到这样的情况:我有“处理器”函数,它们在元素集合上迭代运行,并且还需要知道集合的长度。
另一方面,我有“提供者”功能,可以生成集合,因此已经知道了长度。生成的集合可能是List[T]
,Array[T]
或Set[T]
等,但即使在List[T]
的情况下,我的生成器也知道大小(即使{{1} }}类型不存储它)。
所以我自然会将“处理器”函数声明为采用似乎适合所有集合类型List
的最通用类型作为参数。然而,他们在内部需要通过迭代收集遍历以O(N)的代价找出大小,这是不希望的。
所以我天真的解决方案是创建一个像Iterable[T]
这样的新类型,并让提供者和处理器函数创建并采用这种类型。 IterableWithSize[T]
和Seq[T]
似乎都不符合要求。但这似乎是一个相对常见的用例,所以我怀疑有一种更惯用的方法来做到这一点。那会是什么?
答案 0 :(得分:2)
在Scala集合中,性能敏感的方法(如size
)不是从特征继承而是在底部类型中重写。例如,请参阅immutable.HashSet
:
所以你不需要关心它。只需定义一个高级别的共同特征,例如Traversable
或Iterable
,您就完成了。
答案 1 :(得分:2)
实际上,没有惯用的方法。 Scala集合实际上是要以其他规定的方式遍历或使用(例如Set.contains
或Map.get
)。检查尺寸不是它们的一部分,其中一些甚至不是有限的。
现在,IndexedSeq
是一个相对安全的赌注 - 它保证了O(logn)索引访问,这只有在你有O(logn)大小时才有可能。此外,由于类似原因,Set
和Map
也相当安全。但是如果你正在寻找能够以size
速度保证的特性,那就没有。{/ p>
答案 2 :(得分:1)
Traversable
怎么样?您提及的所有馆藏都会从中继承(Array
间接通过WrappedArray
),并提供size
和toIterable
(或toIterator
)进行遍历。
答案 3 :(得分:1)
我认为没有一种惯用的方法可以做到这一点。但这里有两种选择:
(1)扩展Scala的List / Set / Array集合并覆盖size方法。这并不像第一眼看上去那么困难。
(2)将List / Set / Array集合与大小一起包装,并定义一个隐式的解包器,如:
class IterableWithSizeWrapper[E](private val c: Iterable[E], val size: Int)
object IterableWithSizeWrapper {
implicit def unwrap[E](iws: IterableWithSizeWrapper[E]): Iterable[E] = iws.c
}
object ListWithSizeTest {
def process[E](iws: IterableWithSizeWrapper[E]) {
// iws.size uses your cached size value
// iws.take(i) forces the unwrap to the original collect
// so iws.take(i).size takes the calculated size
for (i <- 0 to iws.size) assert(iws.take(i).size == i)
}
def main(args: Array[String]) {
process(new IterableWithSizeWrapper(List(1,2,3), 3))
process(new IterableWithSizeWrapper(Set(1,2,3), 3))
process(new IterableWithSizeWrapper(Array(1,2,3), 3))
}
}