【问题标题】:Get top 10 and last 10 from a million records从一百万条记录中获取前 10 名和后 10 名
【发布时间】:2015-05-11 16:33:25
【问题描述】:

我有一份显示 2-4 百万条记录的报告。我从 oracle 获取记录到 java 并将其推送到 excel 报告中。这一切都已经完成了!

现在,我还需要添加一个包含前 10 条和最后 10 条记录的新选项卡。最好的方法是什么?

我应该在 java 中使用 PriorityQueue 实现还是使用二叉树来跟踪前 10 名和最后 10 名。我不需要在数据结构中存储十亿条记录。我只需要一次保存10个。 例如:

PriorityQueue<DataObject> queueTop10 = new PriorityQueue<DataObject>(10, topComparator);
PriorityQueue<DataObject> queueLast10 = new PriorityQueue<DataObject>(10, leastComparator);
    while (data is coming from database)
    {
    // push to excel stuff here
    queueTop10 .add(dataObject);   OR binarytreeTop.insert(dataObject)
    queueLast10.add(dataObject);   OR binarytreeLeast.insert(dataObject)
    }

如果我也可以使用其他数据结构,请告诉我。

谢谢

【问题讨论】:

  • “前 10 名”是什么意思?每条记录都有某种分数吗?或者您是否正在寻找最常出现的键值?还是什么?
  • IMO 使用堆仅获取最小元素的工作量较少。树更有组织,但需要更多的计算来维持这种组织。在您的情况下,您需要访问前 10 和后 10 记录,而堆可能不适合您。我相信你应该使用树实现 (TreeMap),额外的开销也许是合理的。
  • 谁会阅读这些报告?如此多的记录开始进入“如果我们把这份报告的一页给这个国家的每个人......”或“如果我们把这些页面堆叠起来,我们将有一堆 X% 的方式到达月球”的领域。另外,OutOfMemoryError.
  • 嘿!感谢您及时的回复。我真的很抱歉错字。我的意思是 2-4 百万条记录,而不是十亿条记录。我们将其保存为 CSV 格式,并将其划分为不同的输出文件。
  • 是的,有分数。所以,topComparator 和 leastComparator 实现了逻辑。

标签: java binary-tree binary-search-tree priority-queue


【解决方案1】:

热门算法使用最小堆(Java 中的PriorityQueue),但您的算法中应该有一些大小检查。假设每个项目都有一个分数,你想收集分数最高的 10 个项目。 PriorityQueue 有效地公开具有最低分数的项目:

PriorityQueue<DataObject> top = new PriorityQueue(10, comparator);
for (DataObject item : items) {
  if (top.size() < 10) top.add(item);
  else if(comparator.compare(top.peek(), item) < 0) {
    top.remove();
    top.add(item);
  }
}

【讨论】:

    【解决方案2】:

    您可以使用优先级队列,因为它的作用类似于 Java 中的堆。见How does Java's PriorityQueue differ from a min-heap? If no difference, then why was it named PriorityQueue and not Heap?

    【讨论】:

      【解决方案3】:

      PriorityQueue&lt;T&gt; 将无法按原样处理您的代码,因为构造函数中的 10 是初始容量;您的队列将随着您的移动而增长到 1B 项。

      但是,TreeSet&lt;T&gt; 可以使用,只需稍作修改。每次队列增长超过 10 时,您需要添加删除第 11 项的代码:

      TreeSet<DataObject> top10 = new TreeSet<DataObject>(topComparator);
      TreeSet<DataObject> bottom10 = new TreeSet<DataObject>(leastComparator);
      while (data is coming from database) {
          top10.add(dataObject);
          if (top10.size() == 11) {
              top10.pollLast();
          }
          bottom10.add(dataObject);
          if (bottom10.size() == 11) {
              bottom10.pollLast();
          }
      }
      

      【讨论】:

      • 您好,非常感谢您的快速回复!如果我能够将 priorityQueue 管理为仅包含 10 个元素,如下所示,您认为哪种数据结构会更高效/更快。
      • @user1797559 我认为根本不会有任何区别,因为队列很小。实际上,您可能会将其更改为一个数组并对十个项目进行线性搜索而没有看到任何差异(这是在内存中的随机位置进行 3 次比较,而在内存中的连续位置进行了 10 次比较,因此参考位置可能会缩小差距为你)。如果您使用 30..50 个元素,故事可能会有所不同,但对于 10 个元素,这可能无关紧要。
      • @user1797559 PriorityQueue 与大多数堆一样,是用数组实现的,因此您可以通过它获得引用的位置。因为数组太小,扫描数组可能仍然更快(尽管有大 O 缩放),因为代码非常简单。
      【解决方案4】:

      Excel 电子表格中有 40 亿条记录?不,你不会https://superuser.com/questions/366468/what-is-the-maximum-allowed-rows-in-a-microsoft-excel-xls-or-xlsx

      您应该在数据库上执行此操作,而不是依赖 java 实现。对于这么多的记录,它的效率肯定低于优化的数据库查询。

      【讨论】:

      • 嘿!感谢您及时的回复。我真的很抱歉错字。我的意思是 2-4 百万条记录,而不是十亿条记录。我们将其保存为 CSV 格式,并将其划分为不同的输出文件。我不想在 DB 上做,因为排序逻辑有点复杂,所以查询需要很多连接。由于我已经获得了一次数据,我认为如果我可以使用相同的数据并使用比较器 topComparator 和 minimumComparator 提取前 10 条和至少 10 条记录,它会更快。请让我知道你的想法。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-12-28
      • 2016-03-06
      • 2013-06-14
      • 2021-09-01
      • 1970-01-01
      • 2015-12-14
      • 1970-01-01
      相关资源
      最近更新 更多