在我的应用程序中,我需要创建动态类/类型/对象。我通常会使用HashMap<K,V>
来解决它(感谢与反射相反的简单性),例如:
class DynObject {
private HashMap<String,Object> fields;
}
但是,我需要创建很多这些对象 - 我的意思是很多(我的内存已经不足了)。
我有一个想法 - 有相同字段的实例的组(&#34;类型和#34;)因此保持每个实例上的映射是多余的。为了减少每个实例的大小,我尝试将HashMap<K,V>
移到单独的类:
class DynamicType {
private HashMap<String, Integer> fields = new HashMap<>();
private String name;
public HashMap<String, Integer> getFields() { return fields; }
}
class Dynamic {
private Object[] values;
private DynamicType type;
public Dynamic(DynamicType ofType)
{
type = ofType;
values = new Object[type.getFields().size()];
}
public Object getField(String name) {
return values[type.getFields().get(name)];
}
public void setField(String name, Object value) {
values[type.getFields().get(name)] = value;
}
}
...哪种方法可以完美运行,但还有另一个问题 - 我需要不时地在类型上添加需求字段,在这种情况下,DynamicType
字段的更改(仅添加)不会回顾性地反映其Dynamic
个实例
我事先并不知道有多少课程,他们的名字或他们的领域,而且现有课程的额外字段需要被发现&#34;在任何时候。整体实际存储的数据量很大
tl; dr:如何使用字段String-&gt; Object创建动态类型以保持实例上的内存开销最小化?
我最终使用了接受的答案和使用延迟值分配实现自己的LazyList<T>
的想法的结合,测试显示它节省了高达50%的内存(使用各种类型的5个字段进行测试)。
答案 0 :(得分:1)
执行此操作最有效的方法可能是:
这样,DynamicType可以在需要时扩展每个实例。它看起来像这样:
class DynamicType {
private final String name;
private Map<String, Integer> fields = new HashMap<>();
private List<Dynamic> instances = new ArrayList<>();
public DynamicType(String name) {
this.name = name;
}
public Dynamic createInstance() {
Dynamic d = new Dynamic(this);
for (int i=0;i<fields.size();i++) {
d.addField(null); //Some default value for uninitialized fields
}
instances.add(d);
return d;
}
public void addField(String name, Object defaultValue) {
fields.put(name, fields.size());
for (Dynamic d : instances) {
d.addField(defaultValue);
}
}
protected Integer getFieldIdx(String name) {
return fields.get(name);
}
}
class Dynamic {
private List<Object> values;
private DynamicType type;
protected Dynamic(DynamicType ofType)
{
type = ofType;
values = new ArrayList<>();
}
protected void addField(Object value) {
values.add(value);
}
public Object getField(String name) {
return values.get(type.getFieldIdx(name));
}
public void setField(String name, Object value) {
return values.set(type.getFieldIdx(name), value);
}
}
用法示例:
DynamicType t = new DynamicType("t");
t.addField("name", null);
t.addField("age", null);
Dynamic t1 = t.createInstance();
Dynamic t2 = t.createInstance();
t.addField("sex", "male"); //Now both t1 and t2 have sex -> male
P.S。代码未经过测试,但理论上应该有效。
旧回答:
如果我正确理解了您的问题,则会复制String
个密钥,因为大多数字段在“动态对象”上都有相同的名称。
因此,最好的解决方案是只为这些字符串创建一个单独的索引,以便所有HashMaps共享同一个实例。您可能会在写入操作上获得额外的开销,但是在密钥出现问题时可以节省大量内存。
这样的事情(没有OOP,但这个想法应该是显而易见的):
Map<String, String> fieldNameIndex = new HashMap<>();
void addEntry(Map<String, Object> map, String name, Object value) {
String existingName = fieldNameIndex.get(name);
if (existingName == null) {
fieldNameIndex.put(name, name);
existingName = name;
}
map.put(existingName, value);
}
基本上,此代码维护所有已使用密钥的内部索引,如果索引中未找到现有密钥,则分配新密钥。因此,如果使用相同的键创建N个对象,它们将引用相同的String实例。
此外,根据新对象出现的频率和此操作的执行速度 - 您可能希望将索引切换到TreeMap。如果不断添加新的字段名称,HashMaps将表现得更好。但是,如果在初始加载后很少添加新键 - TreeMap可能会表现得更好。