使用Smalltalk中其他集合选择对象的最快方法

时间:2011-07-31 10:27:41

标签: optimization collections smalltalk

鉴于两个集合:

srcCollection := #('Lorem' 'ipsum' 'dolor' 'sit' 'amet,' 'consectetur' 'adipisicing' 'elit,' 'sed' 'do' 'eiusmod' 'tempor' 'incididunt' 'ut' 'labore' 'et' 'dolore' 'magna' 'aliqua.' 'Ut' 'enim' 'ad' 'minim' 'veniam,' 'quis' 'nostrud' 'exercitation' 'ullamco' 'laboris' 'nisi' 'ut' 'aliquip' 'ex' 'ea' 'commodo' 'consequat.' 'Duis' 'aute' 'irure' 'dolor' 'in' 'reprehenderit' 'in' 'voluptate' 'velit' 'esse' 'cillum' 'dolore' 'eu' 'fugiat' 'nulla' 'pariatur.' 'Excepteur' 'sint' 'occaecat' 'cupidatat' 'non' 'proident,' 'sunt' 'in' 'culpa' 'qui' 'officia' 'deserunt' 'mollit' 'anim' 'id' 'est' 'laborum').      
objCollection := #('Lorem' 'numquam' 'eius' 'modi' 'tempora' 'incidunt' 'ut' 'labore' 'et' 'dolore' 'magnam' 'aliquam' 'ipsum' 'dolor' 'ex' 'ea' 'commodo' 'consequat.' 'Duis' 'aute' 'irure' 'dolor' 'in' 'reprehenderit' 'in' 'voluptate' 'velit' 'esse' 'cillum' 'dolore' 'eu' 'fugiat' 'nulla' 'pariatur.' 'Excepteur' 'sint' 'occaecat' 'cupidatat' 'non' 'proident,' 'sunt' 'in' 'culpa' 'qui' 'officia' 'deserunt' 'mollit' 'anim' 'id' 'est' 'laborum' 'Sed' 'ut' 'perspiciatis' 'unde' 'omnis' 'iste' 'natus' 'error' 'sit' 'voluptatem' 'accusantium' 'doloremque' 'laudantium,' 'totam' 'rem' 'aperiam,' 'eaque' 'ipsa' 'quae' 'ab' 'illo' 'inventore' 'veritatis' 'et' 'quasi' 'architecto' 'sit' 'amet,' 'consectetur' 'adipisicing' 'elit,' 'sed' 'do' 'eiusmod' 'tempor' 'incididunt' 'ut' 'labore' 'et' 'dolore' 'magna' 'aliqua.' 'Ut' 'enim' 'ad' 'minim' 'veniam,' 'quis' 'nostrud' 'exercitation' 'ullamco' 'laboris' 'nisi' 'ut' 'aliquip' 'beatae' 'vitae' 'dicta' 'sunt' 'explicabo.' 'Nemo' 'enim' 'ipsam' 'voluptatem' 'quia' 'voluptas' 'sit' 'aspernatur' 'aut' 'odit' 'aut' 'fugit,' 'sed' 'quia' 'consequuntur' 'magni' 'dolores' 'eos' 'qui' 'ratione' 'voluptatem' 'sequi' 'nesciunt.' 'Neque' 'porro' 'quisquam' 'est,' 'qui' 'dolorem' 'ipsum' 'quia' 'dolor' 'sit' 'amet,' 'consectetur,' 'adipisci' 'velit,' 'sed' 'quia' 'non' 'quaerat' 'voluptatem.' 'Ut' 'enim' 'ad' 'minima' 'veniam,' 'quis' 'nostrum' 'exercitationem' 'ullam' 'corporis' 'suscipit' 'laboriosam,' 'nisi' 'ut' 'aliquid' 'ex' 'ea' 'commodi' 'consequatur?' 'Quis' 'autem' 'vel' 'eum' 'iure' 'reprehenderit' 'qui' 'in' 'ea' 'voluptate' 'velit' 'esse' 'quam' 'nihil' 'molestiae' 'consequatur,' 'vel' 'illum' 'qui' 'dolorem' 'eum' 'fugiat' 'quo' 'voluptas' 'nulla' 'pariatur?').

其中objCollection保证包含srcCollection中的所有元素。注意:在我的应用程序中,objCollection实际上是包含这些字符串作为标识符而没有重复项的复杂对象。

我一直在测量并尝试优化选择objCollection中同样位于srcCollection中的所有对象。使用带有堆栈VM的Pharo 1.2中的[ 1000 timesRepeat: [ ... ] ] timeToRun和带有2Gb内存的Windows XP时,以下时间是以毫秒为单位。这些是我的尝试:

objCollection intersection: srcCollection
7537
7507

objCollection select: [: str | srcCollection includes: str ]
7471
7507

srcCollection collect: [: str | objCollection detect: [: obj | obj = str ] ]
4227
4323

有更快的方法吗?

3 个答案:

答案 0 :(得分:4)

前两个做同样的事情:Collection >> #intersection:的实现是self select: [:each | aCollection includes: each]

Collection >> #intersection:最终使用self anySatisfy: [:x | x = mySearchObj ]来完成其工作,该工作使用#do:遍历集合。 #detect:最终会做同样的事情。

我怀疑你看到的差异不是因为三者中的任何一个都比另一个更有效,而是垃圾收集之类的产品。

鉴于此,我选择#intersection:因为它的语义清晰。它说的是你想要的,而不是其他两个,你只看到你如何得到你想要的东西,并且必须推断/推断出意图。

答案 1 :(得分:1)

如果您能够将asSet添加到这两个集合中,您可能会做得更快。我从1480年到197年。

答案 2 :(得分:0)

两个集合都有重复。

srcCollection size ~= srcCollection asSet size.
objCollection size ~= objCollection asSet size.

如果您想处理重复项,假设您通过<另一种可能性是使用这种方法(成本n1 * log(n1)+ n2 * log(n2)+ n1 + n2而不是n1 * n2用于天真交叉)

Collection>>sortedIntersection: aCollection
    "Answer the intersection of two collections, sorted by < and accounting duplicates."
    | intersection obj objStream src srcStream |
    srcStream := self sorted readStream.
    objStream := aCollection sorted readStream.
    intersection := (Array new: self size) writeStream.
    [srcStream atEnd | objStream atEnd] whileFalse:
        [src := srcStream next.
        obj := objStream next.
        [src = obj] whileFalse:
            [[src < obj] whileTrue: [srcStream atEnd ifTrue: [^intersection contents]. src := srcStream next].
            [obj < src] whileTrue: [objStream atEnd ifTrue: [^intersection contents]. obj := objStream next]].
        intersection nextPut: src].
    ^intersection contents

这也可以使用Heap,并删除第一个元素,但速度很慢。

heapSortedIntersection: aCollection
    "Answer the intersection of two collections, sorted by < and accounting duplicates."

    | intersection obj src objHeap srcHeap |
    srcHeap := Heap withAll: self.
    objHeap := Heap withAll: aCollection.
    intersection := (Array new: self size) writeStream.
    [srcHeap isEmpty | objHeap isEmpty] whileFalse:
        [src := srcHeap removeFirst.
        obj := objHeap removeFirst.
        [src = obj] whileFalse:
            [[src < obj] whileTrue: [srcHeap isEmpty ifTrue: [^intersection contents]. src := srcHeap removeFirst].
            [obj < src] whileTrue: [objHeap isEmpty ifTrue: [^intersection contents]. obj := objHeap removeFirst]].
        intersection nextPut: src].
    ^intersection contents

最后,如果您没有可用的总订单,您可以简单地使用Bag,仍然考虑重复

bagIntersection: aCollection
    "Answer the intersection of two collections, accounting duplicates."

    | objBag absentTag |
    objBag := Bag withAll: aCollection.
    absentTag := Object new.
    ^self reject: [:each | (objBag remove: each ifAbsent: [absentTag]) == absentTag]