【问题标题】:Object pool of singleton-like objects单例对象的对象池
【发布时间】:2023-04-05 05:31:01
【问题描述】:

我已经编写了一个类似单例的不可变对象池的实现(“单例”是指我只需要每个具有相同字段值的对象的一个​​副本),如下所示。我不喜欢我必须创建对象只是为了检查池中是否已经有等效对象的事实。有没有办法避免这种情况? (即检查我是否已经在池中拥有我需要的对象,如果它不存在则只创建它)。

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

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);
    }
}

【问题讨论】:

    标签: java singleton pool


    【解决方案1】:

    您能否在字段上使用嵌套的HashMap,因为它们都是int 类型?

    Map<Integer, Map<Integer, Parent>> pool = new HashMap<>();
    

    然后您可以有一个方法来查看是否存在缓存的单例(我当时没有 IDE,所以请多多包涵):

    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;
        }
    }
    

    【讨论】:

    • 我相信这种方法可行,但需要为每个主字段中的每个值创建一个嵌套映射,并为每个辅助字段中的每个值创建另一个嵌套映射,等等(最多字段数- 1) 这似乎比仅仅创建一个临时对象更糟糕......
    • 是的,我的假设是您只有两个字段。解决此问题的一种方法是创建一个包装器对象,其中包含您想要的所有字段,然后将其存储到您的 Child 类中。这可以将地图中的嵌套简化为一层。
    • 这当然也可以,但这需要为每个子类(或至少为每组参数)创建另一个类并实例化它以便在地图中查找它,即正是我试图避免的......(实际上会使代码进一步复杂化)
    【解决方案2】:

    您的泳池地图的密钥必须更改为其他内容。我会尝试对子对象的字段进行哈希处理并将其用作键。因此,当我必须使用这些字段检查子项是否存在时,我只需要计算字段的哈希值并在池中查找它。

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

    static final String fieldSeparator = "somevalue";

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

    这个想法是从字段中创建一个唯一的键。

    【讨论】:

    • 我实际上尝试过,但正如其他人提到的,哈希值不是唯一的,因此查找某个对象可能会获得不同类的对象。
    • 在单次执行期间,Object.hashcode() 始终返回相同的值,即根据 Object 类 javadocs 的约定。
    • 这是正确的,但不一定是唯一值。也就是说,两个不同的对象可能具有相同的哈希值。如果在将第二个对象插入地图时发生这种情况,它将覆盖第一个对象,然后当您尝试获取第一个对象(使用哈希值)时,您实际上会得到第二个。
    猜你喜欢
    • 2015-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-15
    相关资源
    最近更新 更多