我不能使用数据库,因为我必须使用代码来解决特定比赛的在线问题。
对于少量数据,我可以在没有任何问题的情况下使用哈希表。但是当我的数据变得很大时,我会耗尽堆大小。我无法更改堆大小,因为我只能上传代码,而且我不能提供工作环境。这是挑战。
If the strings are repeated often, have natural language frequences, do not use new object instances for the same string.
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;
}
A numbering of the strings probably is too slow.
Packing the list of strings in a single one (separator some control character), and possibly Gzipping the String (GZipOutputStream, GZipInputStream).
Tune the hash map with a sufficient initial capacity. (Sorry if I state the obvious.)
Do your own allocation of all ArrayLists, using huge large 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);
}
There are map implementations using primitives like int and long; the trove library for instance (did not use it myself).
使用简单的数组而不是 ArrayList
可以节省一些额外的内存(但并不多)。
如果搜索性能不是优先考虑因素,可以使用一个 Pair<Integer, List<>>
并手动进行搜索。
如果整数范围有限,只需实例化一个 List[integer_range]
的数组并使用数组索引作为键。
由于您正在使用 Strings
,可以尝试对它们进行 intern()
处理,并确保没有重复值。
请告诉我们关于数据的统计信息 - 键是什么,值是否重复等等。
一些想法
如果可以写入文件,就将数据存储在那里。你可以在内存中保留键的集合以加快查找速度,然后只写出值——可以是单个文件,也可以是每个条目一个文件。
创建自己的映射实现,将值列表序列化为字符串或byte[],然后压缩序列化数据。但每次进行get/put操作时都会导致大量运行时开销。请参见 http://theplateisbad.blogspot.co.uk/2011/04/java-in-memory-compression.html 以获取示例。
每次查找映射数据时,仅计算列表值而不存储它们——如果可以的话。
一种可能的优化方法是使用ArrayList.trimToSize,它可以将ArrayList所使用的存储空间减少到最小。
你可以将ArrayList以序列化(甚至压缩)的ByteBuffers存储。当需要访问列表时,您需要对其进行反序列化、更改/读取并将其存回。
操作会显著变慢,但您可以做一些缓存以保留X个ArrayList在堆中,并在外部存储其余部分。
如果您无法增加堆大小,则需要限制哈希表(或任何其他数据结构)的大小。我建议尝试使用Apache LRUMap:
LRUMap
这是一个 Map 的实现,它具有最大大小,并使用最近最少使用算法从 Map 中删除项目,当达到最大大小并添加新项目时。
如果您确实需要同步版本,则也可以使用以下方式获得:
可以通过以下方式获得同步版本:Collections.synchronizedMap( theMapToSynchronize ) 如果将被多个线程访问,则必须同步访问此 Map。即使是并发的 get(Object) 操作也会产生不确定的行为。
如果您不想失去使用 LRU 数据,则需要编写算法将一些数据保留在数据结构中,其余数据存储在持久性存储中,例如文件等。