Java 字符串的值对象
我有一个类似于Java 字符串的值对象,java,string,memory,Java,String,Memory,我有一个类似于mapidmap=newhashhap的映射其中键是一个特定的Id(我们处理许多Id),我们称之为MyEntityId 因此,为了可读性和避免使用此映射时出现问题,我想将其转换为更好的:map idMap=newhashmap 现在,使用lombock实现MyEntityId的方法是: @Data @AllArgumentsConstructor public class MyEntityId { private String id; } 问题是,现在每次我有一个表示M
mapidmap=newhashhap的映射
其中键是一个特定的Id(我们处理许多Id),我们称之为MyEntityId
因此,为了可读性和避免使用此映射时出现问题,我想将其转换为更好的:map idMap=newhashmap
现在,使用lombock实现MyEntityId的方法是:
@Data
@AllArgumentsConstructor
public class MyEntityId {
private String id;
}
问题是,现在每次我有一个表示MyEntityId的字符串时,我都必须将其转换为新的MyEntityId(myString)
,这样做,我就失去了字符串内部表示的优势。因此,如果在字符串“123abc”之前,JVM中只有一个对象,那么现在调用新MyEntityId(id)
构造函数时,我就有了同样多的对象
如何解决此问题?您必须重写
hashCode()
和equals()
方法:
public class MyEntityId {
private String id;
@Override
public int hashCode() {
return id.hashCode();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyEntityId that = (MyEntityId) o;
return id.equals(that.id);
}
}
注:
- 这是纯java实现。考虑使用.
- 我觉得这有点过于工程化,我个人会使用字符串索引映射
- 解决可读性问题的另一种方法是给地图起一个“更好”的名字
有些选择可能是:
Map anyClassValueByMyEntityIDMap=newhashmap()代码>
Map anyClassValueForMyEntityIDMap=newhashmap()代码>
Map myEntityId到anyClassValueMap=new HashMap()代码>
通过这种方式,您可以清楚地看到,您必须使用MyEntityID
访问映射,而不会失去字符串内部表示的优势。您可以始终像Java那样处理字符串-保留现有实例的映射,并使用工厂方法:
public class MyEntityId {
private static Map<String, MyEntityId> instances = new HashMap<>();
private MyEntityId(String id) { ... }
public static MyEntityId valueOf(String id) { /* Get or create instance for that ID */ }
}
公共类MyEntityId{
私有静态映射实例=new HashMap();
私有MyEntityId(字符串id){…}
公共静态MyEntityId值(字符串id){/*获取或创建该id的实例*/}
}
您仍然不应该依赖于==
,但同样,您也不应该依赖==
来处理字符串
但我不确定是否值得这么麻烦。Java被设计成更喜欢冗长而不是性能(其理念是“购买更好的硬件比雇佣更好的程序员更便宜”)。如果您同意这种推理,那么只使用包装器并仅在有证据表明存在问题时进行优化可能是有意义的;如果您不同意,使用另一个答案中建议的好名字可能就足够了。以什么方式HashMap
比HashMap
更好?您知道必须使用什么作为访问该地图的密钥。您不必在map声明中添加注释说“嘿,这个字符串实际上是一个MyEntityId”,也不必让其他开发人员(甚至明天的您)四处寻找用法来理解这个键是什么。其他语言已经解决了这个问题。。。例如Scala有案例类。嗯,我不确定Scala案例类在这方面有什么帮助-您仍然需要调用MyCaseClass(theString)
。也许你的意思是类型别名type MyEntityId=String
那么您要为系统中的每种类型的字符串创建一个包装器类型吗?让我想起ADA,你想要的是“typedef”,但Java没有。您的解决方法是添加一个新的包装器对象,该对象至少为每个键添加16个字节,以及访问它的一些运行时开销,再加上更新包装器对象的开销。或者您可以添加一条注释并接受Java限制。重写equals和hashcode将得到:newmyentityid(“abc”)==newmyentityid(“abc”)
??我不这么认为……当然不。但它只会在您的地图中保留一个MyEntityId(“abc”)实例。是的,这是肯定的。我提出的问题是,我不会有太多MyEntityId对象始终映射同一个键…是的,但你知道,这个id命名问题也存在于方法签名中,当然好的名称会有所帮助,但我想知道是否有可能做更多的面向对象的事情(例如,如果没有MyEntityId对象作为参数,则不能调用my方法)这就是我所想的…但为了避免地图增长过多,我应该有一个策略,不时从地图中逐出元素,类似于垃圾收集器,这让我使用缓存,比如番石榴,而不是简单的地图,整个事情肯定变得过度工程化…我认为这仍然是一个好的意图。