我想存储键值对,其中键是一个整数,值为ArrayLists
Strings
。
我无法使用数据库,因为我必须使用代码来解决特定比赛的在线问题。
对于少量数据,我可以毫无问题地使用哈希表。 但是当我的数据变大时,我的堆大小就会耗尽。我无法更改堆,因为我只需要上传代码而无法提供工作环境。 这就是挑战。
答案 0 :(得分:3)
如果字符串经常重复,有自然语言频率,请不要对同一个字符串使用新的对象实例。
private Map<String, String> sharedStrings = new HashMap<>().
public void shareString(String s) {
String t = sharedStrings.get(s);
if (t == null) {
t = s;
sharedStrings.put(t, t);
}
return t;
}
字符串的编号可能太慢了。
将字符串列表打包在一个字符串中(分隔一些控制字符), 并且可能正在对字符串进行Gzip(GZipOutputStream,GZipInputStream)。
调整具有足够初始容量的哈希映射。 (对不起,如果我说明显了。)
使用巨大的String[]
:
int count;
String[] allStrings = new String[999999];
Map<Integer, Long> map = new HashMap<>(9999);
void put(int key, List<String> strings) {
int start = count;
for (String s : strings) {
allStrings[count] = s;
++count;
}
// high: start index, low: size
long listDescriptor = (((long)start) << 32) | (count - start);
map.put(key, listDescriptor);
}
有使用int和long等原语的地图实现;例如trove库(我自己没有使用它)。
答案 1 :(得分:1)
使用简单数组而不是ArrayList
可以节省一些额外的内存(但不多)。
如果搜索效果不是优先考虑事项,您可以使用Pair<Integer, List<>>
并手动进行搜索。
如果整数范围有限,只需实例化一个List[integer_range]
数组并使用数组索引作为键。
由于您使用的是Strings
,因此您可以尝试intern()
,并确保没有重复值。
让我们知道您所拥有的数据的统计信息 - 关键是什么,值是否重复等等。
答案 2 :(得分:0)
一些想法
如果你可以写一个文件存储那里的数据。您可以将密钥保存在内存中以便更快地查找,并只写出值 - 可以是单个文件,也可以是每个条目的文件。
创建自己的地图实现,将值列表序列化为String或byte [],然后压缩序列化数据。您必须对读取进行反序列化。每次你执行get / put时,你都会为此获得很大的运行时间。有关示例,请参阅http://theplateisbad.blogspot.co.uk/2011/04/java-in-memory-compression.html。
每次查询地图数据时,只需每次计算列表值而不是存储它们 - 如果可以的话。
答案 3 :(得分:0)
一种可能的优化可能是ArrayList.trimToSize,它将ArrayList使用的存储空间减少到最小。
答案 4 :(得分:0)
您可以将ArrayList存储为序列化(甚至可以压缩)ByteBuffers。当您需要访问列表时,您需要对其进行反序列化,更改/读取它,然后将其存储回来。
操作速度会明显变慢,但您可以进行一些缓存以将X Arraylists保留在堆中并将其余部分存储在外部。
答案 5 :(得分:-1)
如果无法增加堆大小,则需要限制哈希表(或您使用的任何其他数据结构)的大小。我建议尝试Apache LRUMap:
LRUMap
具有最大大小的Map的实现,并使用最近最少使用的算法从Map中删除项目 达到最大尺寸并添加新项目。
如果您确实需要同步版本,那么也可以使用:
可以通过以下方式获得同步版本: Collections.synchronizedMap(theMapToSynchronize)如果是的话 由多个线程访问,您必须同步对此的访问 地图。即使并发的get(Object)操作也会产生不确定性 行为。
如果您不想放松使用LRU,那么您需要编写一个算法来保存数据结构中的某些数据并将其放在持久存储中,例如文件等。