访问此对象的最快方法

时间:2012-03-22 21:27:12

标签: java data-structures collections

假设我有一个1,000,000个用户的列表,其唯一标识符是他们的用户名字符串。因此,为了比较两个用户对象,我只是覆盖compareTo()方法,比较用户名成员。

鉴于用户名字符串,我希望从列表中找到User对象。在一般情况下,这是最快的方式。

我猜一个HashMap,将用户名映射到User个对象,但我想知道是否还有其他一些我不知道哪个会更好。

4 个答案:

答案 0 :(得分:6)

如果您不需要将它们存储在数据库中(这是常见的情况),HashMap<String, User>可以正常工作 - 它的查找复杂度为O(1)。

如上所述,通常的情况是将它们放在数据库中。但为了获得更快的结果,使用了缓存。您可以使用EhCache - 它类似于ConcurrentHashMap,但它具有元素的生存时间和分布在多台机器上的选项。

您不应该将整个数据库转储到内存中,因为它很难同步。您将面临使地图中的条目无效并使其保持最新的问题。缓存框架使这一切变得更容易。另请注意,数据库有自己的优化,并且您的用户不可能保留在内存中以便更快地访问。

答案 1 :(得分:0)

我确定你想要一个哈希映射。它们是最快的东西,而且内存效率很高。正如其他回复中所指出的,String作为一个很好的键,所以你不需要覆盖任何东西。 (以下情况也是如此。)

主要替代方案是 TreeMap 。这是较慢的,并使用更多的内存。然而,它更加灵活。相同的地图将有5个条目和500万个条目。你不需要提前提示。如果您的列表大小变化很大,TreeMap将根据需要获取内存,并在不需要时将其释放。哈希映射不是那么好放手,正如我在下面解释的那样,当抓住更多内存时,它们可能会很尴尬。

TreeMap可以更好地与垃圾收集器配合使用。他们在一个容易找到的小块中寻求记忆。如果你开始一个有100,000个条目空间的哈希表,当它被填满时,它将释放100,000个元素(几乎是64位机器上的一个兆位)数组,并要求一个甚至更大的数组。如果它重复这样做,它可以超越GC,这往往会抛出内存异常,而不是花费大量时间来收集和集中分散的空闲内存。 (它更倾向于保持其速度声誉而牺牲机器拥有大量内存的声誉。你真的可以设法耗尽内存,90%的堆未使用,因为它是碎片化的。)

因此,如果您正在运行程序完全倾斜,那么您的名称列表大小会有很大差异 - 甚至您可能还有几个名称大小不一的名称列表 - TreeMap将会运行对你好多了。

哈希映射无疑将是您所需要的。但是当事情变得非常疯狂时,就会出现 ConcurrentSkipListMap 。这是TreeMap的一切,除了它有点慢。另一方面,它允许多个线程无添加,更新,删除和读取,没有同步。 (我提到它只是为了完整。)

答案 2 :(得分:0)

就数据结构而言,HashMap可能是一个不错的选择。它有利于更大的数据集。插入时间被认为是常数O(1)。

在这种情况下,听起来你将执行比插入更多的查找。对于查找,平均时间复杂度为O(1 + n / k),这里的关键因素(抱怨双关语)是散列算法在桶中均匀分布数据的效果。

这里的风险是用户名的长度很短,并使用一个小的字符集,如a-z。在这种情况下会有很多冲突导致HashMap加载不均匀,从而减慢了查找速度。改善这种情况的一个选择可能是创建自己的用户密钥对象,并使用适合您的密钥的algorthim覆盖hashcode()方法。

总结如果你有一个大型数据集,一个好的/合适的散列算法,并且你有空间将它全部保存在内存中,那么HashMap可以提供相对快速的查找

我认为鉴于你在ArrayList上的最后一篇文章,它是scalabilty我会采取Bozho的建议,并去寻找目的构建缓存,如EhCache。这将允许您控制内存使用和驱逐策略。仍然比数据库访问快很多。

答案 3 :(得分:0)

如果您不经常更改用户列表,则可能需要使用Aho-Corasick。您将需要一个预处理步骤,该步骤将花费O(T)时间和空间,其中T是所有用户名的长度之和。之后,您可以在O(n)时间内匹配用户名,其中n是您要查找的用户名的长度。因为你必须查看你正在寻找的用户名中的每个字符,我认为不可能做得比这更好。