一个HashMap<String, Integer>
的大小为50,000;而且,它不能同时适合记忆。我希望将这张大表分成int chunkSize = 1024
大小的小表。结果,我试图编写一个方法,但我天真的方法是迭代大表并创建一个小方法。然而,天真的迭代方法是O(n)
并且它是开放的bug,因为它没有使用内置的Java方法,只是迭代表。您是否有解决此问题的方法,以便解决方案具有时间效率,并且更依赖于Java内置方法。
更新:我将使用这些较小的hashMap来提供给Pipeline系统。管道系统采用管道设计模式设计。对于每个阶段,将应用一些String操作和Text数据挖掘算法。拆分Big HashMap将为旧应用程序和未来的管道系统增加价值。实际上,分流操作现在是管道系统的一部分;但是,对于旧的应用程序,我已经开始阅读如何微调HashMap内部结构。
答案 0 :(得分:2)
由于您在评论中提到要存储单词频率/出现次数,我建议使用以下数据结构:
使用树。树中的每个节点都将包含一个字母,并具有频率值。 Root将是空单词的表示,每个节点将表示从根开始的单词。在这个树中,查找/更新频率需要与单词一样多的步骤,与单词的数量无关。
如果这棵树对于内存来说太大了,那么一个简单的分区将是树中的第一个级别,这是每个单词的第一个字母。您可以将其存储在不同的文件中。
如果您需要更精细的粒度,可以使用第一个字母作为文件夹名称,第二个字母作为文件名在这些文件夹中等。
答案 1 :(得分:2)
如果您使用HashMap<String, Integer>
来表示单词频率,则代表50,000个条目应该没有问题。如果有,那么显而易见的解决方案是增加Java堆大小。 (使用-Xmx
命令行选项执行此操作。)
(如果我的心算是正确的,HashMap的开销类似于每个条目8个单词......加上键和值对象占用的空间。在你的情况下,这可能会增加~25个单词,包括假设典型英语单词的关键和值。在32位JVM中总共大约5Mb。)
但是要回答您的问题,Java标准库中没有用于将HashMap
拆分为较小HashMaps
的方法或类。
答案 2 :(得分:1)
如果您有单词频率,则值得使用可变长度以避免重复创建新对象。这不会对使用的内存产生太大影响,但会降低GC。
public static void main(String... ignored) {
StringBuilder sb = new StringBuilder();
// start with as much free memory as possible.
System.gc();
long start = memoryUsed();
Map<String, long[]> frequencyMap = new HashMap<>();
int keys = 50 * 1000;
for (int i = 0; i < keys; i++) {
sb.setLength(0);
sb.append("word-").append(i);
String key = sb.toString();
long[] count = {i};
frequencyMap.put(key, count);
}
long used = memoryUsed() - start;
System.out.printf("To create a map with " + frequencyMap.size() + " key/values used %,d KB%n", used / 1024);
}
public static long memoryUsed() {
return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
}
使用-Xmn1g -XX:-UseTLAB
打印
To create a map with 50000 key/values used 6,895 KB
除非你有一个7 MB远远的系统,否则我会把这么小的地图放在一块。