我有一个内存中的键值存储(大小可能最多1GB),其中将String
映射到String
。
到目前为止,它已实现为Map<String, String>
。
但是,在极少数情况下,我需要映射到字符串列表,因此需要将其更改为Map<String, List<String>>
。
由于这种情况不常见(可能少于%1),因此我正在讨论是否将这些用例分为两个不同的图。
有没有人知道在映射中所有列表仅包含一个元素而不是直接拥有String
对象的情况下,我应该期望多少开销(内存占用和CPU)?
谢谢!
答案 0 :(得分:1)
可能性(以增加内存占用的顺序):
Map<String, String> map = new HashMap<>(); // Concatenated string values
List<String> get(String key) {
return Arrays.asList(map.getOrDefault(key, "").split("\f"));
}
Map<String, String[]> map = new HashMap<>();
private static final String[] EMPTY = new String[0];
List<String> get(String key) {
return Arrays.asList(map.getOrDefault(key, EMPTY));
}
Map<String, List<String>> map = new HashMap<>(); // LinkedList
List<String> get(String key) {
return map.get(key);
}
(只是示例代码。我对空字符串的处理不好。)
如其他人所说,测量空间和速度。还应考虑将Set<String>
而不是List
作为更优化的数据结构。考虑Collections.singletonList("...")
和emptyList()
。
如果字符串大部分是Latin-1,请考虑Java 9使用更紧凑的字节数组(与Java 8相反)。
对于大字符串,您可以使用byte[]
压缩为GZipOutputStream
。
最后一个选择是,用尽java -Xmx
和物理内存:使用数据库。
答案 1 :(得分:1)
正如其他人已经建议的那样,只有通过测量,您才能得到肯定的答案(对于给定的机器/ JVM组合)。但是可以预测至少一些结果。
除了Joop的建议之外,我还可以想象几种不同的方法:
使用简单的Map<String, List<String>>
,ArrayList
或类似的通用列表,然后您将获得一个附加的(相当胖的)包装对象,每个包装对象包括一个字符串数组(可能为128个字节)地图条目。开箱即用的实现,但是浪费了很多内存。
使用Map<String, List<String>>
,并确保将单字符串值包装在Collections.singletonList()
或类似的紧凑构造中。然后,每个单个字符串将获得一个附加的包装对象(16到32个字节)。开销较小,但是在插入单个字符串时需要特殊对待。
使用两个映射,一个用于单个字符串的Map<String, String>
,另一个用于多字符串大小写的Map<String, List<String>>
。几乎没有开销,但是在插入条目以及查询/迭代地图时都需要特殊对待。
Joop的串联字符串解决方案将两个或更多String
实例折叠成一个更长的String
,从而消除了它们各自的开销。这甚至导致“负”开销,但是在插入条目以及查询/迭代地图时都需要特殊处理。 String
拆分将在检索条目时消耗一点点额外的运行时间,即使对于单字符串情况也是如此。 [尽管String.split()
是基于正则表达式的,但一般情况下它们都很慢,但Joop的解决方案与String.split()
实现中的“快速路径”匹配-对Joop表示敬意!]
现在,选择就是您了。