是否认为在集合类型之间进行转换是不好的形式?

时间:2012-11-02 02:21:37

标签: java list collections bag

我希望这个问题具体到足以被认为适合StackOverflow。我查看了常见问题解答,我认为这符合资格,因为它具体且与编程有关。

我正在Java中实现复杂的数据挖掘算法(FP-growth)。算法的一些初始阶段要求我扫描大型数据库并保持找到的每个项目类型的运行计数。这似乎非常适合Hashbag接口。我在Apache Commons中找到了一个似乎对我有用的东西。

所以现在,我的HashBag充满了[itemType,count]条目(对)。稍后在算法中,我需要在这些对上做很多类似列表的操作。在某些情况下,我必须按itemType对集合进行排序。在其他人中,我必须按计数排序。这似乎非常适合List接口。

我得出结论,我必须将我的Hasbag转换为List。但它在某种程度上感觉很脏,就像浪费空间和时间。是否有一种更聪明的方法可以做到这一点,或者是否存在编程问题是常见的情况,你必须在不同的时间以不同的方式对待你的收藏,转换是必要的恶魔?

另一种方法是制作我自己的界面,这是一个真正的列表,但允许“包式”添加。每次我想添加一些东西时,我必须保持列表排序并使用自定义比较器执行二进制搜索。构建该集合可能比构建Hashbag需要更长的时间,但我会在最后保存转换步骤。有什么想法更好吗?

谢谢!

4 个答案:

答案 0 :(得分:3)

我假设您正在使用Apache Commons Collections HashBag类。您是否考虑过使用TreeBag?它实现了相同的Bag接口,但可以根据您提供的比较器有效地保存数据。

那就是说,当你需要改变排序顺序时,通常没有比使用不同比较器将集合复制到新集合更好的答案。

答案 1 :(得分:3)

如果您使用Guava's Multiset而不是Apache的Bag - 大致类似,但风格不同 - 您无需转换即可完成大部分操作。 Multiset.entrySet()返回Set<Entry<E>>,其中Entry<E>有效地表示一对元素和一个计数 - 听起来这可能是满足您对元素计数操作的最佳方法对,也许?您可以迭代,就像迭代Map.entrySet()一样。

您可以使用Multisets.copyHighestCountFirst(Multiset)以最高频率优先顺序重新排序多重邮件,并使用TreeMultiset直接按元素排序。

(披露:我向Guava捐款。)

答案 2 :(得分:2)

  

然而,它在某种程度上感觉很脏,就像浪费空间和时间一样。是否有一种更聪明的方法可以做到这一点,或者是否存在编程问题是常见的情况,你必须在不同的时间以不同的方式对待你的收藏,转换是必要的恶魔?

有时需要在集合类型之间进行转换。如果有必要“脏”或“不优雅”或“哑巴”并不真正相关。

预先过度思考这些事情也是错误的。实际的计算权衡往往难以掌握。例如,如果您将HashBag更改为TreeBag,则插入从O(1)转到O(logN),但您可以避免分类和复制的开销。 “大喔”分析/思考不会给你一个明确的答案。实际上,真正的表现取决于比例因子,N的值,包中命中和未命中的比例等。

我建议以显而易见的方式尝试实现,并查看它是否运行良好......如果没有,请对其进行分析以查看数据结构是否是主要瓶颈。然后基于分析,和输入数据集的其他测量,找出从基线实现中提高性能的最佳方法。

答案 3 :(得分:0)

回答我自己的问题!

我做了一些试验,由Louis Wasserman提到的Guava图书馆提供的不同类型的Multiset。在我的特定测试用例中,我正在解析一个1GB的XML文件(书籍和作者数据库)并创建一个非常大的Multiset(保持每个作者在数据库中出现的次数)。一旦我到达解析结束,我需要获得一个新的Multiset,它只包含出现超过x次的作者,其中x是某个阈值。我还希望我的最终作品按作者姓名排序。

以下是我尝试过的两种不同方式(其中包括):

1)收集TreeMultiset中的原始计数,然后删除任何不符合阈值的计数 2)在HashMultiset中收集原始计数,然后创建一个新的TreeMultiset,我在其中添加哈希集中的每个项目,其计数满足阈值

尽管有转换和额外的内存使用,但第二种方式证明明显更快(大约25%)。显然,很大一部分是从二叉树中删除它是非常低效的。

所以这里明确的结论是,在这种情况下,转换是一个很好的举动(除非你有内存限制,不允许它)。

再次感谢你让我进入番石榴图书馆,路易斯!