Java:通知提供者的实现与 hashCode 驱动的 Map

发布于 2024-08-10 22:23:37 字数 2106 浏览 4 评论 0原文

我已经为通用侦听器 E 的通知群实现了抽象通用提供程序,后代必须使用特定的通知代码覆盖 notifyListener(E) 。对于侦听器的后备列表,我选择 WeakHashMap。侦听器必须作为弱引用保存:

abstract public class NotificationProvider<E> {

    private Map<E, Object> listeners = new WeakHashMap<E, Object>();

    public addListener(E listener) {
        listeners.put(listener, null);
    }

    public void notifyListeners() {
        for (E listener: listeners.keySet())
            notifyListener(listener);
    }

    abstract protected void notifyListener(E listener);
}

典型用途:

    NotificationProvider<MyListener> provider;
    provider = new NotificationProvider<MyListener>() {
        @Override
        protected void notifyListener(MyListener listener) {
            listener.myNotification();
        }
    }
    provider.addListener(myListener1);
    provider.addListener(myListener2);
    provider.notifyListeners();

一切正常,但是当我需要 AbstractList 后代类作为侦听器时,支持 WeakHashMap 仅接受一个侦听器实例!很明显 - 侦听器上的方法 hashCode()equals() 为所有实例(空列表)返回相同的值,因此 WeakHashMap.put > 只替换之前添加的监听器。

    public class MyList extends AbstractList<MyItem> {
        // some implementation
    }
    NotificationProvider<MyList> provider;
    provider = new NotificationProvider<MyList>() {
        @Override
        protected void notifyListener(MyList listener) {
            // some implementation
        }
    }
    MyList list1 = new MyList();
    MyList list2 = new MyList();
    provider.addListener(list1);
    provider.addListener(list2); 
    provider.notifyListeners();  // only list2 instance is notified

最好的解决方案是什么?

  1. 使用另一个非hashCode支持集合 - 但是WeakHashMap对我来说太棒了,因为自动为我管理弱引用

  2. 使用非通用侦听器,例如带有简单equals() { return (this == object); 的抽象类; } 实现——但这不是那么灵活

  3. 使用一些包装器来为带有简单 equals() 的侦听器 - 但这个包装器对于 addListener(E) 调用者来说不能是透明的,因为弱引用

还有其他想法吗?

I have implemented abstract generic provider for notification bunch of generic listeners E, descendants have to override notifyListener(E) with specific notification code. For backing list of listeners I choose WeakHashMap<K,V>. Listeners must be held as weak references:

abstract public class NotificationProvider<E> {

    private Map<E, Object> listeners = new WeakHashMap<E, Object>();

    public addListener(E listener) {
        listeners.put(listener, null);
    }

    public void notifyListeners() {
        for (E listener: listeners.keySet())
            notifyListener(listener);
    }

    abstract protected void notifyListener(E listener);
}

Typical use:

    NotificationProvider<MyListener> provider;
    provider = new NotificationProvider<MyListener>() {
        @Override
        protected void notifyListener(MyListener listener) {
            listener.myNotification();
        }
    }
    provider.addListener(myListener1);
    provider.addListener(myListener2);
    provider.notifyListeners();

Everything works well, but when I need AbstractList descendant class as listener, backing WeakHashMap accepts only one listener instance! It's clear -- methods hashCode() and equals() on listeners return same value for all of instances (empty lists), so WeakHashMap.put only replace previously added listener.

    public class MyList extends AbstractList<MyItem> {
        // some implementation
    }
    NotificationProvider<MyList> provider;
    provider = new NotificationProvider<MyList>() {
        @Override
        protected void notifyListener(MyList listener) {
            // some implementation
        }
    }
    MyList list1 = new MyList();
    MyList list2 = new MyList();
    provider.addListener(list1);
    provider.addListener(list2); 
    provider.notifyListeners();  // only list2 instance is notified

What is the best solution?

  1. use another non-hashCode backing collection -- but WeakHashMap is so sweet for me, because automatically managing weak references for me

  2. use non-generic listener, for example abstract class with simple equals() { return (this == object); } implementation -- but this is not so flexible

  3. use some wrapper for listeners with simple equals() -- but this wrapper cannot be transparent to addListener(E) caller due to weak references

Another ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

默嘫て 2024-08-17 22:23:37

WeakHashMap 有点损坏。它使用弱密钥,但不使用身份哈希。除非您的键类型的 equals()hashCode() 使用“identity”,否则不应使用 WeakHashMap。相反,您需要的是 WeakHashMapIdentityHashMap 的组合。

一种可能性是使用 MapMaker。如果密钥较弱或软,它会自动使用密钥的身份哈希/相等性。例如:

ConcurrentMap<K, V> myMap = new MapMaker().weakKeys().makeMap();

WeakHashMap is kind of broken. It uses weak keys, but it doesn't use identity hashing. Unless the equals() and hashCode() of your key type use "identity", you shouldn't use WeakHashMap. Instead you need something that's a combination of WeakHashMap and IdentityHashMap.

One possibility is to use MapMaker from Google Collections. It automatically uses identity hashes/equality for keys if the keys are weak or soft. eg:

ConcurrentMap<K, V> myMap = new MapMaker().weakKeys().makeMap();
过气美图社 2024-08-17 22:23:37

问题的关键似乎是您的侦听器实现是子类化 AbstractList,但没有覆盖 equals() / hashCode()。我强烈建议反对这种类型的继承(实现继承),因为它违反了 OO 原则(多态可替换性原则)。

最好实现一个自定义侦听器类,该侦听器类可能会引用 AbstractList(如果需要),并且还提供自己的 equals()hashCode( ) 实现。

The crux of the problem seems to be that your listener implementation is subclassing AbstractList, but not overriding equals() / hashCode(). I would strongly recommend against this type of inheritance (implementation inheritance) as it violates OO-principles (principle of polymorphic substitutability).

It would be far better to implement a custom listener class that possibly references an AbstractList if it requires one, and that also provides its own equals() and hashCode() implementations.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文