Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/397.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 我可以使用什么符号表来存储~50 mil字符串并进行快速查找,而不会耗尽堆空间?_Java_Memory - Fatal编程技术网

Java 我可以使用什么符号表来存储~50 mil字符串并进行快速查找,而不会耗尽堆空间?

Java 我可以使用什么符号表来存储~50 mil字符串并进行快速查找,而不会耗尽堆空间?,java,memory,Java,Memory,我有一个大约5000万字符串的文件,需要在启动时添加到某种符号表中,然后以合理的速度搜索几次 我尝试使用DLB trie,因为由于所有字符串都小于10个字符,所以查找速度相对较快,但在填充DLB时,我会得到超出GC开销限制或outofmemory-堆空间错误。HashMap也发现了相同的错误。这是为了一个将由分级器编译和运行的作业,所以我宁愿不只是分配更多的堆空间。是否有一种不同的数据结构可以减少内存使用,同时仍有合理的查找时间?如果您希望前缀共享较少,那么trie可能不是您的最佳选择 由于在启

我有一个大约5000万字符串的文件,需要在启动时添加到某种符号表中,然后以合理的速度搜索几次


我尝试使用DLB trie,因为由于所有字符串都小于10个字符,所以查找速度相对较快,但在填充DLB时,我会得到超出GC开销限制或outofmemory-堆空间错误。HashMap也发现了相同的错误。这是为了一个将由分级器编译和运行的作业,所以我宁愿不只是分配更多的堆空间。是否有一种不同的数据结构可以减少内存使用,同时仍有合理的查找时间?

如果您希望前缀共享较少,那么trie可能不是您的最佳选择

由于在启动时只加载一次查找表,并且您的目标是以“合理的速度”降低内存占用,因此最好的选择可能是使用排序数组和二进制搜索进行查找

首先,将数据加载到数组中。由于您可能事先不知道大小,因此可以将其加载到
ArrayList
。然后从列表中提取最后一个数组

假设您加载5000万个10个字符串,内存将为:

10 character string:
    String: 12 byte header + 4 byte 'hash' + 4 byte 'value' ref = 24 bytes (aligned)
    char[]: 12 byte header + 4 byte 'length' + 10 * 2 byte 'char' = 40 bytes (aligned)
    Total: 24 + 40 = 64 bytes
Array of 50 million 10 character strings:
    String[]: 12 byte header + 4 byte 'length' + 50,000,000 * 4 byte 'String' ref = 200,000,016 bytes
    Values: 50,000,000 * 64 bytes = 3,200,000,000 bytes
    Total: 200,000,016 + 3,200,000,000 = 3,400,000,016 bytes = 3.2 GB
ArrayList
转换为
String[]
时,需要另一份
String[]
Arrays.sort()
操作可能需要50%的数组大小(~100000000字节)来进行临时存储,但是如果在排序之前为GC释放了
ArrayList
,则可以重用该空间

因此,仅对于符号表,总需求约为3.5 GB

现在,如果空间真的很宝贵,你可以压缩它。如您所见,
字符串本身在64个字节中增加了24个字节的开销。您可以使符号表直接使用
char[]

此外,如果您的字符串都是
US-ASCII
ISO-8859-1
,则可以将
char[]
转换为
byte[]
,节省一半字节

这将使值大小从64字节减少到32字节,并将符号表的总大小从3.2 GB减少到1.8 GB,或者在加载期间大约减少2 GB


更新

假设字符串的输入列表已经排序,下面是如何进行排序的示例。作为一个示例,它只使用一个小的静态数组作为输入,但您可以轻松地从文件中读取它们

public class Test {
    public static void main(String[] args) {
        String[] wordsFromFile = { "appear", "attack", "cellar", "copper",
                                   "erratic", "grotesque", "guitar", "guttural",
                                   "kittens", "mean", "suit", "trick" };
        List<byte[]> wordList = new ArrayList<>();
        for (String word : wordsFromFile) // Simulating read from file
            wordList.add(word.getBytes(StandardCharsets.US_ASCII));
        byte[][] symbolTable = wordList.toArray(new byte[wordList.size()][]);

        test(symbolTable, "abc");
        test(symbolTable, "attack");
        test(symbolTable, "car");
        test(symbolTable, "kittens");
        test(symbolTable, "xyz");
    }
    private static void test(byte[][] symbolTable, String word) {
        int idx = Arrays.binarySearch(symbolTable,
                                      word.getBytes(StandardCharsets.US_ASCII),
                                      Test::compare);
        if (idx < 0)
            System.out.println("Not found: " + word);
        else
            System.out.println("Found    : " + word);
    }
    private static int compare(byte[] w1, byte[] w2) {
        for (int i = 0, cmp; i < w1.length && i < w2.length; i++)
            if ((cmp = Byte.compare(w1[i], w2[i])) != 0)
                return cmp;
        return Integer.compare(w1.length, w2.length);
    }
}
使用单个字符数组存储所有字符串(已排序),并使用整数数组存储偏移量。字符串n是从
offset[n-1]
(包含)到
offset[n]
(独占)的字符<代码>偏移量[-1]
为零

对于char数组,内存使用量为1GB(50M*10*2),对于offset数组,内存使用量为200MB(50M*4)。非常紧凑,甚至有两个字节的字符

为了不超过堆空间,您必须通过合并较小的排序字符串数组来构建此数组。但一旦你有了它,它应该相当快


或者,您可以尝试内存优化的trie实现,例如。这不仅使用前缀共享,还使用公共后缀的结构共享,因此除非字符串完全随机,否则它应该非常紧凑。看。但是它是scala,而不是java。

Trie应该非常有效,如果您的字符串共享许多前缀。它们是什么样的弦?你确定你的密码实现吗?IRL,最好的答案可能是:@Jean BaptisteYunès这是一个所有可能密码的列表,所以前缀没有太多模式。。也许这让崔错了选择?trie实现在较小的数据集上运行良好。@3802400您肯定不是指所有可能的密码吗?如果是这样,如果密码可以由最多9个字母组成(正如您所说的@KlitosKyriacou,所有可能的密码都有几个限制。为了分配密码,这比现实生活中的密码限制更容易,因为您可以再解释一下如何合并较小的字符串数组吗?我是否必须在合并字符串数组的同时构建偏移数组?如果您的同事mpact字符串数组总是被排序的,合并两个数组很简单。只需使用类似的算法并合并到生成器中。您可能希望使用大字符串而不是字符数组。然后您可以使用StringBuilder.StringBuilder.toString()创建字符串的副本。如果空间太少,您不想将空间要求增加一倍。@KlitosKyriacou同意。使用StringBuilder/string是快速运行的好方法,而不是完美的解决方案。也就是说,连接字符串的内存使用率只有1G,我不会因为复制而失眠。我创建ArrayList时内存不足,因此我计算了输入大小并尝试创建字符串数组,但内存也不足。我能够创建字符串的字节数组(已按顺序排序),但我在思考如何搜索字节数组时遇到了问题。