gpt4 book ai didi

Java - 检查是否存在对 Map 中对象的引用

转载 作者:塔克拉玛干 更新时间:2023-11-02 08:05:34 25 4
gpt4 key购买 nike

几周前,我编写了一个具有以下行为的 Java 类:

  • 每个对象包含一个最终整数字段
  • 该类包含一个静态映射(Key:Integer,Content:MyClass)
  • 每当类的对象被实例化时,如果静态映射中已经存在具有所需整数字段的对象,则进行查找:返回它,否则创建一个并将其放入映射中。

作为代码:

public class MyClass
{
private static Map<Integer, MyClass> map;
private final int field;

static
{
map = new HashMap<>();
}

private MyClass(int field)
{
this.field = field;
}

public static MyClass get(int field)
{
synchronized (map)
{
return map.computeIfAbsent(field, MyClass::new);
}
}
}

这样我就可以确定,每个整数(作为字段)只存在一个对象。我目前担心,这会阻止 GC 收集我不再需要的对象,因为对象始终存储在映射中(存在引用)...

如果我像这样写一个类似循环的函数:

public void myFunction() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
MyClass c = MyClass.get(i);
// DO STUFF
}
}

在调用该方法后,我最终会在内存中得到 Integer.MAX_VALUE 对象。有没有一种方法可以检查是否存在对 map 中对象的引用并删除它们?

最佳答案

这看起来像是 the multiton pattern 的典型案例: 你最多想拥有一个 MyClass 的实例对于给定的 key 。但是,您似乎还想限制创建的实例数量。 lazily instantiating 很容易做到这一点你的MyClass您需要的实例。此外,您想清理未使用的实例:

Is there a way I can check, whether references to objects in the map exists and otherwise remove them?

这正是 JVM 的 garbage collector是为了;当 Java 核心库已经提供 tools for marking certain references as "not strong" 时,没有理由尝试实现您自己的“垃圾收集”形式。 ,即只有在某处有强引用(即在 Java 中,“正常”引用)引用给定对象时才应引用给定对象。

使用 Reference 实现对象

而不是 Map<Integer, MyClass> , 你应该使用 Map<Integer, WeakReference<MyClass>>Map<Integer, SoftReference<MyClass>> : 两者 WeakReference SoftReference 允许 MyClass如果没有对该对象的强(读作:“正常”)引用,则它们引用的实例将被垃圾收集。两者之间的区别在于,前者在所有强引用消失后在下一次垃圾收集操作时释放引用,而后者仅在“必须”时才释放引用,即在 JVM 方便的某个时刻(参见 related SO question)。

此外,无需同步整个 Map : 你可以简单地使用 ConcurrentHashMap (实现 ConcurrentMap ),which handles multi-threading in a way much better than by locking all access to the entire map .因此,您的 MyClass.get(int)可能看起来像这样:

private static final ConcurrentMap<Integer, Reference<MyClass>> INSTANCES = new ConcurrentHashMap<>();

public static MyClass get(final int field) {
// ConcurrentHashMap.compute(...) is atomic <https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html#compute-K-java.util.function.BiFunction->
final Reference<MyClass> ref = INSTANCES.compute(field, (key, oldValue) -> {
final Reference<MyClass> newValue;
if (oldValue == null) {
// No instance has yet been created; Create one
newValue = new SoftReference<>(new MyClass(key));
} else if (oldValue.get() == null) {
// The old instance has already been deleted; Replace it with a
// new reference to a new instance
newValue = new SoftReference<>(new MyClass(key));
} else {
// The existing instance has not yet been deleted; Re-use it
newValue = oldValue;
}
return newValue;
});
return ref.get();
}

最后,在上面的评论中,您提到您会 "prefer to cache maybe up to say 1000 objects and after that only cache, what is currently required/referenced" .尽管我个人看不出有什么(好的)理由,但可以执行 eager instantiation在“第一个” 1000 个对象上,将它们添加到 INSTANCES创作 map :

private static final ConcurrentMap<Integer, Reference<MyClass>> INSTANCES = createInstanceMap();

private static ConcurrentMap<Integer, Reference<MyClass>> createInstanceMap() {
// The set of keys to eagerly initialize instances for
final Stream<Integer> keys = IntStream.range(0, 1000).boxed();
final Collector<Integer, ?, ConcurrentMap<Integer, Reference<MyClass>>> mapFactory = Collectors
.toConcurrentMap(Function.identity(), key -> new SoftReference<>(new MyClass(key)));
return keys.collect(mapFactory);
}

如何定义哪些对象是“第一个”对象取决于您;在这里,我只使用整数键的自然顺序,因为它适用于一个简单的示例。

关于Java - 检查是否存在对 Map 中对象的引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38292955/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com