【问题标题】:What Algorithm is used by java.util.HashSet and java.util.TreeSet to store unique values in its structure?java.util.HashSet 和 java.util.TreeSet 使用什么算法在其结构中存储唯一值?
【发布时间】:2017-10-22 01:54:21
【问题描述】:

我遇到过Flajolet-Martin算法、HyperLogLog等多种算法从元素列表中找出唯一元素,突然好奇Java是如何计算的?在每种情况下存储和查找唯一值的时间复杂度是多少?

【问题讨论】:

  • java.util.Set 是一个接口,而不是一个实现。 JDK 库中有两个常用的实现:java.util.TreeSet 和 java.util.HashSet。他们都没有使用 HyperLogLogs。
  • 我也不确定这将如何适用于 java.util.Set 接口,因为 API 需要保留所有元素,并且它需要唯一性。 HyperLogLog 算法会在 multi 集合(一个包)中有太多元素无法同时保存在内存中时估计其基数。
  • 就在名字里。 HashSet 使用哈希表。 TreeSet 使用树。
  • Flajolet-Martin 算法或 HyperLogLog 都不适合 Map 数据结构。它们是关于计数流中不同的元素。
  • 为什么不自己看看呢? JDK 是开源的。来源是here

标签: java set time-complexity big-o hyperloglog


【解决方案1】:

Flajolet-MartinHyperLogLog 算法是关于在 N 元素流的一次传递中获得不同元素(count-distinct problem)的近似计数,时间为 O(N)和适度的(比O(N) 好得多)内存使用。

Map API 的实现不需要解决“count-distinct”问题。

(旁白:TreeMapHashMap已经保留地图中条目数量的预计算计数1;即Map.size()。前提是您不要陷入线程安全问题,结果是准确的(不是近似的)。调用size() 的成本是O(1)。维护它的成本是O(U),其中U 是映射添加的数量和已执行删除操作。)

更一般地说,Flajolet-Martin 算法或 HyperLogLog 不/不能形成 Map 数据结构的基础。他们没有解决dictionary problem

HashMapTreeMap 使用的算法(分别)是哈希表和二叉树算法。相应的 javadocs 中有更多详细信息,您可以随时查看完整的源代码(带有 cmets)。 (例如,Google for "java.util.HashMap" source ...。)


1 - 有趣的是,ConcurrentHashMap 不能以这种方式工作......因为更新 size 字段将成为并发瓶颈。相反,size() 操作是O(N)

【讨论】:

    【解决方案2】:

    HashSet 类型使用哈希表(通常使用封闭寻址)跟踪其元素,TreeSet 类型使用二叉搜索树跟踪其元素。这些数据结构对“这个元素在这里吗?”这个问题给出了准确的答案。并且对于您需要 100% 确定您以前是否看过某些内容并且它们的内存使用量通常与目前看到的元素总数成正比的情况很有用。

    另一方面,像 HyperLogLog 这样的基数估计器可以很好地回答“有多少不同的元素,给还是取几个百分点?”这样的问题。它们非常适合您需要粗略估计您看到的不同事物的数量,而将所有内容放入哈希表或二叉搜索树之类的方法会占用太多内存(例如,如果您'是一个谷歌网络服务器,你想计算访问你的不同 IP 地址),因为他们使用的内存量通常是你可以提前获取的。但是,他们不允许您回答“我以前见过这件事吗?”形式的问题。因此不能作为任何java.util.Set 子类型的实现。

    简而言之,这里的数据结构旨在解决不同的问题。传统的 BST 和哈希表用于精确查询,目标是确定您是否已经看到某些东西,并且您希望能够迭代所有看到的元素。基数估计器很好,您只关心有多少不同的元素,您不关心它们是什么,并且您不需要确切的答案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-04-30
      • 2022-12-18
      • 1970-01-01
      • 2015-11-25
      相关资源
      最近更新 更多