我正在努力将Roguelike Dungeon冒险游戏移植到Android。 http://tyrant.sourceforge.net
当我开始我的程序时,我首先初始化世界及其所有对象。我有一个包含300万(!)HashMap条目的类。当我尝试初始化该类时,在Android上运行需要5分钟以上,这是一段非常难以接受的时间。一旦课程初始化,游戏运行良好,非常有趣。
所以我想我可以序列化这个对象并在运行时重新加载它。我序列化它,并将其打包在.APK文件中,它的大小超过5MB。 Android只是窒息并给我一个OutOfMemory错误。
我可以反序列化类的一部分(800KB),然后动态创建其余的条目。这仍然需要花费几分钟才能完成,它仍然需要从序列化数据中创建300万个条目。但我知道我的序列化代码工作正常。
如何在Android上存储这个非常大的类文件并在以后加载?我可以探索哪些选项?让我的用户等待5分钟让应用程序启动并不是很好。
这是原始源代码中令人不安的类: http://tyrant.cvs.sourceforge.net/viewvc/tyrant/tyrant/mikera/engine/Lib.java?view=markup
令人不安的是:
private transient Map types;
(我知道这是短暂的)
最终存储超过300万个条目!
答案 0 :(得分:0)
我认为你的部分问题是你的参赛作品太胖了。查看代码,为每个Thing存储一个字符串键控属性的HashMap?那可能就是在那里杀了你。首先要做的是切换到基于枚举的HashMap,看看是否会产生性能差异[并且总和适用于所有的所有字符串 - 用枚举替换它们到处都有,并且具有将它们转换为字符串的实用程序需要显示给用户]
现在,为什么你有300万条款?!?!是你的整个世界?如果是这样的话,你可以把它分成100个方格棋盘,写出100个不同的文件,然后开始只加载你的英雄所在的方块,而后来在后台加载其余的?
答案 1 :(得分:0)
问题的一部分是types
地图中的内容。
从我可以看出,它实际上是Map<String, Map<Integer, List>>
,其中第二级映射实际上是非稀疏数组。如果您选择了更好的数据结构,它将占用更少的空间,您可以更快地构建它。 (我认为你不应该将它序列化......因为在游戏过程中看起来似乎没有改变。)
HashMap<Integer, Object>
占用的空间比Object[]
多。
更好的数据结构是Map<String, LevelMap>
,其中LevelMap
是这样的:
public class LevelMap {
private static final ArrayList<String> EMPTY = Collections.emptyList();
private ArrayList<String>[] levels;
private int firstLevel;
public LevelMap(int firstLevel, int lastLevel) {
this.firstLevel = firstLevel;
levels = new ArrayList<String>[lastLevel - firstLevel + 1];
}
public static void addToTypeMap(String key, String value,
int firstLevel, int lastLevel) {
LevelMap l = types.get(key);
if (l == null) {
l = new LevelMap(firstLevel, lastLevel);
types.put(key, l);
}
l.add(value, firstLevel, lastLevel);
}
public void add(String value, int firstLevel, int lastLevel) {
ensure(firstLevel, lastLevel);
for (int i = firstLevel; i <= lastLevel; i++) {
j = i - this.firstLevel;
if (levels[j] == null) {
levels[j] == new ArrayList<String>();
}
levels[j].add(value);
}
}
private void ensure(int firstLevel, int lastLevel) {
// Make sure that these levels are in the levelmap
// If necessary, reallocate this.levels and adjust this.firstLevel
}
public String get(int level) {
if (level < this.firstLevel ||
level >= this.firstLevel + levels.length ||
levels[level - this.firstLevel] == null) {
return EMPTY;
} else {
return levels[level - this.firstLevel];
}
}
}
答案 2 :(得分:0)