按名称有效访问字段

时间:2012-07-30 21:44:23

标签: java performance optimization

关于我正在努力实现的目标的一些背景:

我正在解析JSON(超过15GB)并且我必须将它存储在内存中,因此不欢迎任何包装器和额外数据,因为其中使用的框架和接口我必须提供按名称访问字段的功能。通过用Enum替换一些String,用int替换一个Integer,用double替换Double等。我能够减少大约90%的内存占用(与Jackson相比)。

我希望通过Java名称在Java运行时有效地访问这些字段。我知道反思,但就我的情况而言,它的表现是不可接受的,所以我不想使用它。

如果它使问题更容易解决,我对设置字段值不太感兴趣。我还在编译时知道支持字段的名称。

由于盒装对象的内存占用,我不想将所有内容存储在地图中,即Map<String,Object>,但我不介意以盒装形式返回它们。

我确信其他人遇到了这个问题,而且我对任何聪明的解决方案感兴趣 - 比if ...其他......陈述更聪明。

假设要实现的接口是:

public interface Accessor {
    Object get(String fieldName);
}

get返回的Object可以是任何类型,包括枚举。一个天真的实现将是:

public class TestObject implements Accessor {

    public enum MyEnum {ONE, TWO, THREE};

    private final MyEnum myEnum;
    private final int myInt;
    private final double myDouble;
    private final String myString;

    public TestObject(MyEnum myEnum, int myInt, double myDouble, String myString) {
        this.myEnum = myEnum;
        this.myInt = myInt;
        this.myDouble = myDouble;
        this.myString = myString;
    }

    @Override
    public Object get(String fieldName) {
        if ("myEnum".equals(fieldName)) {
            return myEnum;
        } else if ("myInt".equals(fieldName)) {
            return myInt;
        } else if ("myDouble".equals(fieldName)) {
            return myDouble;
        } else if ("myString".equals(fieldName)) {
            return myString;
        } else {
            throw new UnsupportedOperationException(); // Or could simply return null
        }
    }

}

2 个答案:

答案 0 :(得分:4)

您想要的是从fieldName到值的映射,其类型由fieldName确定。您事先了解了一组字段名称,因此这是Enum的理想任务。

如果您不喜欢将每个字段硬编码为枚举的想法,那么变体将是每个类型的枚举(MY_FIELD1变为MY_ENUM),具有从fieldName到此EnumType的映射。

在下面的代码中,我假设了fieldName和TestObject之间的关系。具体来说,看起来TestObject会呈现各种类型的相同值(当然是合理的),而不是每个字段名称的单独值?

所以,代码:


重写:

@Override
public Object get(String fieldName) {
    MyField field = MyField.mapNameToField(fieldName);
    if (field == null)
        throw new UnsupportedOperationException(); // Or could simply return null
    return field.getValue(this);
}

鉴于(类似):

enum MyField {
  MY_FIELD1("myField1") {
      public Object getValue(TestObject obj) { return obj.myEnum; }
  },
  MY_FIELD2("myField2") {
      public Object getValue(TestObject obj) { return obj.myInt; }
  },
  ...
  ;

  public abstract Object getValue(TestObject obj);
  public String getName() { return name; }

  public static MyField mapNameToField(String name) { return map.get(name); }

  static {
      map = new HashMap<String,MyField>();
      for(MyField value: values()) {
          map.put(value.getName(), value);
      }
  }

  private MyField(String fieldName) { name = fieldName; }

  private String name;
  private static Map<String, MyField> map;
}

答案 1 :(得分:0)

我从未使用过这个,但看起来很有希望:

http://labs.carrotsearch.com/download/hppc/0.4.1/api/

“高性能基元集合(HPPC)库提供了为所有Java基元类型(字节,整数等)模板生成的典型数据结构(列表,堆栈,映射),以节省内存并提高性能。”

特别是,Object {Type} OpenHashMap类可能正是您所需要的:

  1. ObjectByteOpenHashMap
  2. ObjectCharOpenHashMap
  3. ObjectDoubleOpenHashMap
  4. ObjectFloatOpenHashMap
  5. ObjectIntOpenHashMap
  6. ObjectLongOpenHashMap
  7. ObjectShortOpenHashMap
  8. 我想你会将所有7个定义为字段(或者你喜欢它们的任何子集),然后依次探测每个字段以查看该类型的原始值是否存在密钥。如,

    if (byteMap.containsKey(key)) {
      return byteMap.lget(); // last value saved in a call to containsKey()
    } else if (charMap.containsKey(key)) {
      return charMap.lget();
    } else if {
      // and so on... 
    }
    

    请注意,他们有自己的特殊lget()方法调用来优化containsKey()/ get()使用模式,这对于地图来说是典型的。