Java 类单例对象的对象池

Java 类单例对象的对象池,java,singleton,pool,Java,Singleton,Pool,我已经编写了一个类似于单例的不可变对象池的实现(“单例”,在这个意义上,我只需要每个对象的一个具有相同字段值的副本),如下所示。我不喜欢这样的事实,即我必须创建对象只是为了检查池中是否已经有一个等价的对象。有没有办法避免这种情况?(即,检查池中是否已经有我需要的对象,并且仅在不存在该对象时创建该对象) 注意:不同的子类可以有多个不同的字段,或者根本没有字段 public class Parent { protected static final Map<Parent, WeakRe

我已经编写了一个类似于单例的不可变对象池的实现(“单例”,在这个意义上,我只需要每个对象的一个具有相同字段值的副本),如下所示。我不喜欢这样的事实,即我必须创建对象只是为了检查池中是否已经有一个等价的对象。有没有办法避免这种情况?(即,检查池中是否已经有我需要的对象,并且仅在不存在该对象时创建该对象)

注意:不同的子类可以有多个不同的字段,或者根本没有字段

public class Parent {
    protected static final Map<Parent, WeakReference<Parent>> pool = Collections.synchronizedMap(new WeakHashMap<>());
}

public class Child extends Parent
{
    final int field1;
    final int field2;

    private Child(int field1, int field2) {
        this.field1 = field1;
        this.field2 = field2;
    }

    public static Child newChildClass(int field1, int field2) {

        Child child = new Child(field1, field2);
        Child obj = (Child)pool.get(child).get();
        if (obj == null) pool.put(obj = child, new WeakReference<>(child));
        return obj;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Child that = (Child) o;
        return field1 == that.field1 && field2 == that.field2;
    }

    @Override
    public int hashCode() {
        return Objects.hash(getClass(), field1, field2);
    }
}
公共类父类{
受保护的静态最终映射池=Collections.synchronizedMap(new WeakHashMap());
}
公共类子级扩展父级
{
最后的int字段1;
最后的int字段2;
私生子女(int field1、int field2){
此字段1=字段1;
this.field2=field2;
}
公共静态子newChildClass(int field1,int field2){
孩子=新孩子(字段1,字段2);
Child obj=(Child)pool.get(Child.get();
if(obj==null)pool.put(obj=child,newweakreference(child));
返回obj;
}
@凌驾
公共布尔等于(对象o){
如果(this==o)返回true;
如果(o==null | | getClass()!=o.getClass())返回false;
子对象=(子对象)o;
返回field1==that.field1&&field2==that.field2;
}
@凌驾
公共int hashCode(){
返回Objects.hash(getClass(),field1,field2);
}
}

必须将池映射的密钥更改为其他密钥。我会尝试对子对象的字段进行散列,并将其用作键。因此,当我必须检查这些字段是否存在子字段时,我只需要计算字段的哈希值并在池中查找它

我同意你对散列码唯一性的评论。或者,您可以将字段转换为字符串,并附加字段值,以组成用作键的唯一字符串:

static final String fieldSeparator=“somevalue”

String uniqueKey=fieldSeparator+field1.toString()+fieldSeparator+field2.toString()+…


这个想法是从字段中生成一个唯一的键。

如果字段都是
int
类型,您能否在字段上利用嵌套的
HashMap

Map<Integer, Map<Integer, Parent>> pool = new HashMap<>();
Map pool=newhashmap();
然后,您可以使用一种方法来查看缓存的单例是否存在(我当时没有IDE,因此请注意一些语法错误/bug):

public-Child-getChild(int-field1,int-field2){
Map nestedMap=pool.get(field1);
if(nestedMap==null){
//子项不存在,请创建映射
nestedMap=newhashmap();
}
if(嵌套地图包含(字段2)){
//找到缓存的子项,将其返回
返回nestedMap.get(field2);
}否则{
//缓存的子项不存在,请创建并返回它
Child=newChildClass(字段1,字段2);
nestedMap.put(字段2,子项);
pool.put(field1,nestedMap);
返回儿童;
}
}

我确实尝试过,但正如其他人所提到的,散列值不是唯一的,因此查找某个对象可能会获得另一个类的对象。在单个执行过程中,object.hashcode()始终返回相同的值,这是对象类javadocs的契约。这是正确的,但不一定是唯一的值。也就是说,两个不同的对象可能具有相同的哈希值。如果在将第二个对象插入到映射时发生这种情况,它将覆盖第一个对象,然后当您尝试获取第一个对象(使用哈希值)时,您将实际获取第二个对象。我相信这种方法可以工作,但需要为每个主字段中的每个值创建嵌套映射,每个辅助字段中的每个值都有另一个嵌套映射,等等(最多为字段数-1),这似乎比创建临时对象更糟糕……是的,我的假设是只有两个字段。解决这个问题的一种方法是创建一个包含所有所需字段的包装器对象,然后将其存储到子类中。这可以将映射中的嵌套简化为一个级别。这当然也可以,但这需要为每个子类(或至少为每个参数集)创建另一个类,并实例化它,以便在映射中查找它,这正是我试图避免的。。。(这实际上会使代码更加复杂)
public Child getChild(int field1, int field2) {
    Map<Integer, Parent> nestedMap = pool.get(field1);

    if (nestedMap == null) {
        // Child doesn't exist, create map
        nestedMap = new HashMap<>();
    }

    if (nestedMap.contains(field2)) {
        // cached child found, return it
        return nestedMap.get(field2);
    } else {
        // cached child doesn't exist, create and return it
        Child child = newChildClass(field1, field2);
        nestedMap.put(field2, child);
        pool.put(field1, nestedMap);
        return child;
    }
}