2010-01-15 25 views
12

liên quan: Does java have a "LinkedConcurrentHashMap" data structure?lớp thu Thích hợp cho người nghe sự kiện trong Java


Tôi đang tìm kiếm một lớp bộ sưu tập để giữ tham chiếu đến người nghe sự kiện.

Lý tưởng nhất là tôi muốn bộ sưu tập để có các thuộc tính sau (theo thứ tự ưu tiên):

  1. Duy trì trật tự chèn. Những người nghe trước đó có thể hủy bỏ sự kiện, ngăn không cho nó được gửi đến người nghe được thêm vào sau. Điều này sẽ phá vỡ nếu sử dụng một lớp như HashSet có trình vòng lặp có thể trả về các phần tử theo thứ tự sai.
  2. Sử dụng WeakReference s để danh sách người nghe không ngăn người nghe bị thu thập rác.
  3. Bộ sưu tập là Set, vì vậy các bản sao sẽ tự động bị xóa.
  4. Iterator là ảnh chụp nhanh an toàn theo chủ đề của bộ sưu tập, không bị ảnh hưởng bởi việc thêm người nghe mới. Cũng cho phép các sự kiện được phân phối trên nhiều luồng. (Điều này không cần thiết - tôi có thể lặp qua một bản sao của tập hợp thay thế.)

Tôi biết một số lớp đáp ứng một số tiêu chí này. Ví dụ:

  • java.util.LinkedHashSet (# 1 và # 3)
  • java.util.WeakHashMap, bọc bởi Collections.newSetFromMap (# 2 và # 3)
  • javax.swing.event.EventListenerList (cần thêm một số đồng bộ) (# 1 và # 4)
  • java.util.concurrent.CopyOnWriteArraySet (# 1, # 3 và # 4)

Nhưng không có gì với cả # 1 và # 2. Lớp học như thế này tồn tại trong một thư viện ở đâu đó?

+0

Có thực sự điển hình cho đối tượng nghe để thực hiện 'bằng’ không? –

+4

Ngoài ra, công việc của họ là giữ lại sự tham chiếu mạnh mẽ đối với mỗi người nghe? Nếu tôi đã viết một người nghe để đăng nhập các sự kiện khác nhau, và đăng ký nó, tôi chắc chắn sẽ ngạc nhiên khi nó đột nhiên biến mất một vài phút sau đó! –

+0

Không có vấn đề với bằng là: '{Object o = new Object(); WeakReference r1 = new WeakReference (o), r2 = new WeakReference (o); trả về r1.equals (r2);} 'trả về' false' – finnw

Trả lời

6

Bạn có thể sử dụng WeakListeners (xem http://bits.netbeans.org/dev/javadoc/org-openide-util/org/openide/util/WeakListeners.html) và CopyOnWriteArraySet.

  1. Thực hiện phương thức remove(ListenerType listener) trong nguồn sự kiện của bạn.
  2. Trong phương pháp register(SomeListener listener) của bạn, thêm một WeakListener vào bộ sưu tập thay vì:

    listenerCollection.put((ListenerType)WeakListeners.create ( ListenerType.class, listener, this));

Khi người nghe thật được lấy ra từ bộ nhớ, người nghe yếu sẽ được thông báo, và nó sẽ unregister bản thân . (Đây là lý do tại sao nó cần tham chiếu đến nguồn (this) để đăng ký.) Việc hủy đăng ký được thực hiện bằng cách sử dụng sự phản chiếu bằng cách gọi phương thức loại bỏ nguồn.

+0

Vâng, đây là điều tôi đã nghĩ đến. +1 – finnw

0

Bạn có thể quấn từng tham chiếu trình nghe theo số WeakReference và sau đó sử dụng CopyOnWriteArraySet.

+0

Hai vấn đề với điều đó: 1. tài liệu tham khảo được thu thập rác sẽ vẫn còn trong thiết lập mãi mãi (WeakHashMap tự động xóa chúng) và 2. 'WeakReference' không ghi đè' equals() ', vì vậy bạn có thể kết thúc với nhiều tham chiếu đến cùng một người nghe trong tập hợp nhưng bạn không thể nói rằng chúng là bản sao. – finnw

+0

Đúng. Có lẽ bạn có thể khắc phục điều này, tuy nhiên, bằng cách mở rộng CopyOnWriteArraySet để kiểm tra tham chiếu thay vì WeakReference khi bộ sưu tập thay đổi và tự xóa các tham chiếu lỗi thời. Tôi không tin bất cứ điều gì trong Bộ sưu tập tiêu chuẩn cung cấp mọi thứ bạn muốn ngoài hộp. –

0

Bạn có thể mở rộng WeakReference để ghi đè bằng và hashcode, sau đó bạn có thể sử dụng chúng trong LinkedHashSet.

7

Tôi sẽ bắt đầu bằng cách nói rằng bạn có một vài yêu cầu không hợp lý với nhau. Bạn đang tìm kiếm một bộ sưu tập loại bỏ các bản sao và hỗ trợ các tham chiếu yếu, điều này cho tôi biết rằng người nghe có thể xuất hiện và biến mất vào những thời điểm không xác định. Tuy nhiên, bạn muốn duy trì thứ tự chèn và cho phép một người nghe hủy tất cả thông báo tiếp theo. Đối với tôi, điều này nghe có vẻ giống như một công thức cho các lỗi khó tìm, và tôi khuyên bạn nên xem xét lại nó.

Điều đó nói rằng, bạn có một yêu cầu khá nhiều dẫn đến giải pháp: bạn không muốn ConcurrentModificationException có thể đến từ trình lặp bình thường. Có nghĩa là bạn sẽ phải sao chép danh sách gốc. Trên đường đi, bạn có thể kiểm tra và xóa các tham chiếu trống:

// the master list 
List<WeakReference<MyListener>> _list = new ArrayList<WeakReference<MyListener>>(); 

// inside your send-notification method 
List<MyListener> toNotify = new ArrayList<MyListener>(_list.size()); 
Iterator<WeakReference<MyListener>> itx = _list.iterator(); 
while (itx.hasNext()) 
{ 
    WeakReference<MyListener> ref = itx.next(); 
    MyListener lsnr = ref.get(); 
    if (lsnr != null) 
     toNotify.add(lsnr); 
    else 
     itx.remove(); 
} 

// now iterate "toNotify" and invoke the listeners 

Có thể bạn đang lo lắng, nói "Danh sách! Đó là cấu trúc dữ liệu tuyến tính!Tôi không thể sử dụng, chèn là O (N)! "

Vâng, có bạn có thể. Tôi không biết có bao nhiêu người nghe bạn đang có kế hoạch. Nhưng miễn là bạn đang < 100 (và nhiều khả năng là < 100.000), chi phí tìm kiếm tuyến tính để chèn và xóa sẽ không quan trọng.

Viễn cảnh thú vị hơn từ góc độ mã hóa là cách bạn đối phó với tham chiếu yếu. Đây là mã cực kỳ quan trọng khi giao dịch với các đối tượng tham chiếu: mặc dù rất khó có khả năng là người được giới thiệu sẽ được thu thập giữa hai cuộc gọi đến get(), nhưng có thể là

Điều này đưa tôi đến chính mình WeakReference. Bạn sẽ cần phải tạo lớp con của riêng bạn ghi đè các phương thức equals()hashCode() để ủy quyền cho tham chiếu của nó. Tôi nghĩ rằng tôi chỉ có một lớp học như vậy, nhưng dường như không, vì vậy sẽ để nó cho bạn thực hiện.

1

Tập hợp là bộ sưu tập phù hợp để sử dụng với người nghe.

Nếu bạn dựa vào thứ tự chèn của người nghe, thiết kế của bạn bị hỏng. Nó bỏ qua điểm của người nghe là ISOLATED và INDEPENDENT từ những người nghe khác. Sử dụng Bộ thay vì Danh sách.

Nếu bạn dựa vào WeakReferences thiết kế của bạn bị hỏng. Xóa người nghe trong cùng một đối tượng mà bạn đã thêm. SYMMETRY này hỗ trợ READABILITY và MAINTAINABILITY. Để giải quyết các lỗi lập trình của việc hủy đăng ký hủy bỏ của người nghe với các tham chiếu yếu chỉ ẩn vấn đề.

Nếu bạn cung cấp bộ sưu tập của bạn nghe cho các đối tượng khác hơn là đối tượng quan sát của bạn thì thiết kế của bạn bị hỏng. Giữ cho Set riêng tư để hỗ trợ ENCAPSULATION.

Nếu bạn ghi đè bằng và mã băm của người nghe, thiết kế của bạn bị hỏng. Nó ẩn vấn đề của các cuộc gọi hàm không cần thiết. Thay vào đó, hãy ngăn các cuộc gọi không cần thiết. Sau khi tất cả việc so sánh bằng nhau và hashcode của người nghe không phải là cần thiết.

Trong môi trường MULTITHREADING đặt MONITOR trên tài nguyên "người nghe" trong khi thêm, xóa hoặc lặp lại trên đó. Bạn có thể tạo một BẢN SAO CHỨNG NHẬN trước khi bạn lặp lại để tránh một ConcurrentModificationException. Sau đó, việc lặp lại không cần phải được SYNCHRONIZED, nhưng hành động sao chép nên.

Bất kỳ yêu cầu nào khác phải được điều chỉnh hoặc sửa đổi để phù hợp với các tuyên bố này.Bất kỳ thực hành nào khác sẽ dẫn đến mã không thể duy trì, rò rỉ bộ nhớ vì thiếu sự cô lập, độc lập, đóng gói và rõ ràng.

Các vấn đề liên quan