【问题标题】:Fastest way to flush a hashmap to disk into an sorted set将哈希图刷新到磁盘到排序集的最快方法
【发布时间】:2011-08-26 15:33:33
【问题描述】:

我有一个Map<byte[], Element>,我想对其进行排序并将其写入磁盘,这样我就有了一个文件,其中所有元素都通过 Guava 的UnsignedBytes.lexicographicalComparator 按键排序。

我现在正在做的是:

HashMap<byte[], Element> memory;

// ... code creating and populating memory ...

TreeMap<byte[], Element> sortedMap = new TreeMap<byte[], Element>(UnsignedBytes.lexicographicalComparator());
sortedMap.putAll(memory.getMap());

MyWriter writer = new MyWriter("myfile.dat");
for (Element element: sortedMap.values())
    writer.write(element);
writer.close();

可能很难使排序更快(O(nlogn)),问题是我是否可以改进排序列表的导航。理想情况下,我会排序为ArrayList 而不是TreeMap,这样遍历它会非常快。

我曾考虑将 HashMap 放入 ArrayListCollections.sort() 中,但这需要比实际解决方案更多的复制。

有什么想法吗?

编辑:

我在这里添加了我的测试 ArrayList,它的速度提高了 2 倍,但我认为它使用了更多内存。也许一些 cmets 基于这个假设?

// ArrayList-based implementation 2x faster
ArrayList<Element> sorted = new ArrayList<Element>(memory.size());
sorted.addAll(memory.values());

final Comparator<byte[]> lexic = UnsignedBytes.lexicographicalComparator();

Collections.sort(sorted, new Comparator<Element>(){
    public int compare(Element arg0, Element arg1) {
        return lexic.compare(arg0.getKey(), arg1.getKey());
    }
});
MyWriter writer = new MyWriter(filename);

for (Element element: sorted)
    writer.write(element);
writer.close();

【问题讨论】:

  • 您需要改进的主要内容是写入磁盘。这可能比你做的任何事情慢 100 倍。我会使用分析器来检查您将时间花在哪里。
  • 那里几乎没有什么改进,我已经在使用带缓冲的 DataOutputStream,这是一种简单的顺序方法。正如我的微基准测试所示,排序和迭代会产生影响。
  • 为什么说制作一个 ArrayList> (例如)并对其进行排序比构建一个 TreeMap 需要更多的“复制”?
  • "排序算法是一种改进的归并排序(如果低位子列表中的最高元素小于高位子列表中的最低元素,则忽略合并)。该算法提供了保证 n log( n) 性能。此实现将指定的列表转储到数组中,对数组进行排序,并迭代列表,从数组中的相应位置重置每个元素。这避免了尝试排序导致的 n2 log(n) 性能一个链表到位。”所以元素被复制到一个数组中。

标签: java list sorting queue hashmap


【解决方案1】:

您的问题是“有什么想法吗?”。我想我能写的任何东西都会是一个答案。

我遇到了和你一样的问题,并广泛地对这两种解决方案进行了基准测试:使用树形图以便提前对项目进行排序,或者在事后对它们进行排序。我的基准测试显示与您相同的结果。事后排序更快。

我不会担心第二种方法需要更多的复制。首先,越快越好,对吧?如果第二种方法占用更少的 CPU 周期,那就更好了。

如果内存是一个问题,请记住,树图和哈希图在每个项目上占用的内存比 ArrayList 多得多,ArrayList 由简单的对象数组支持。 treemap 或 hashmap 中的每个元素都需要至少一个对象,通常更多。对象有很多开销,32 或更多字节。在平面数组中,每个元素只占用 4 个字节。

我的基准测试表明,一旦数组大小超过几十个字节,从内存分配数组的时间大致与数组的大小成正比。因此,如果 ArrayList 真的很大,分配它可能会很慢。不过,我认为这是更好的选择,只要不存在内存不足的危险。

【讨论】:

  • 实际上,我正在使用 visualvm 检查内存消耗,而树形图实现使用更多内存。我想因为我有大约 500 万个元素,所以数组大约有 20MB。这对我来说非常好。感谢您的洞察力!
猜你喜欢
  • 2022-10-14
  • 1970-01-01
  • 1970-01-01
  • 2010-10-04
  • 2015-09-16
  • 2010-12-14
  • 2022-08-17
  • 2014-10-11
  • 1970-01-01
相关资源
最近更新 更多