WeakHashMap для использования списков слушателем

The May 11, 1999 Tech Tip titled Reference
Objects

introduced the concept of reference objects, but didn’t go into much
depth. In this tip, you’ll learn more about this topic. Basically,
reference objects provide a way to indirectly reference the memory
needed by objects. The reference objects are maintained in a reference
queue (class ReferenceQueue), which monitors
the
reference objects for reachability. Based on the type of reference
object, the garbage collector can free memory at times when a normal
object reference might not be released.

В платформе Java, существуют четыре типа ссылки на объекты. Прямые ссылки типа вы обычно используете, например:

   Object obj = new Object()

Вы можете думать о прямой ссылки как сильные ссылки, которые не требуют дополнительного программирования для создания или доступа к объекту. Остальные три типа ссылки подклассы

Reference
class found in the java.lang.ref package.
Soft references are provided by the SoftReference
class, weak references by the WeakReference
class, and phantom references by PhantomReference.

Soft references act like a data cache. When system memory is low, the
garbage collector can arbitrarily free an object whose only reference
is a soft reference. In other words, if there are no strong references
to an object, that object is a candidate for release. The garbage
collector is required to release any soft references before throwing an
OutOfMemoryException.

Weak references are weaker than soft references. If the only references
to an object are weak references, the garbage collector can reclaim the
memory used by an object at any time. There is no requirement for a low
memory situation. Typically, memory used by the object is reclaimed in
the next pass of the garbage collector.

Phantom references relate to cleanup tasks. They offer a
notification immediately before the garbage collector performs the
finalization process and frees an object. Consider it a way to do
cleanup tasks within an object.

Как простая демонстрация использования WeakHashMap следующие программы WeakTest создает WeakHashMap с одним элементом в нем. Затем она создает второй поток, который ждет на карту, чтобы выплескивать с просьбой о том, что сборщик мусора каждые 1 / 2 секунды. Основной поток ожидает второго потока и до конца.

   import java.util.*;

   public class WeakTest {
     private static Map<String, String> map;
     public static void main (String args[]) {
       map = new WeakHashMap<String, String>();
       map.put(new String("Scott")"McNealey");
       Runnable runner = new Runnable() {
         public void run() {
           while (map.containsKey("Scott")) {
             try {
               Thread.sleep(500);
             catch (InterruptedException ignored) {
             }
             System.out.println("Checking for empty");
             System.gc();
           }
         }
       };

       Thread t = new Thread(runner);
       t.start();
       System.out.println("Main joining");
       try {
         t.join();
       catch (InterruptedException ignored) {
       }
     }
   }

Поскольку не существует сильная ссылки на ключевые только на карте, записи в карте должны быть мусора при первой возможности системы.

Running the program generates the following results:

 Main joining
Checking for empty

There are two important things to point out in this example. First,
normally a call to System.gc() would not be
required. Because the WeakTest
program is lightweight and doesn’t use much memory an explicit call to
the garbage collector is necessary. Second, notice the call to new
String("Scott")
. You might ask why call new
String()
when the end result is the same "Scott"
string? The answer is that if you don’t call new String(),
the reference for the map key will be to the system’s string constant
pool. This never goes away, so the weak reference will never be
released. To get around this, new String("Scott")
creates
a reference to the reference in the string constant pool. The string
contents are never duplicated. They stay in the constant pool. This
simply creates a separate pointer to the string constant in the pool.

A more complicated use of WeakHashMap is to
manage a listener list for a Swing component or data model. This would
not be a general purpose listener list. Instead it would be a weak
listener list. In this case, as long as a strong reference is kept
outside the component or data model, that object will continue to
notify the listener/observer. This helps garbage collection in the
sense that the listener doesn’t prevent the source object (which
registered the listener) from being garbage collected. By default,
listener references are strong references and are not garbage collected
when your method returns. You must remember to remove the listener when
you finish monitoring the situation.


To demonstrate, lets create a WeakListModel that offers a ListModel. The ListModel relies on a WeakHashMap for storing ListDataListener objects.

Помимо импорта, начало определения класса включает в себя класс и местных переменных.


   public class WeakListModel implements ListModel, 
     Serializable {

     private Map<ListDataListener, Object> listenerList =
       Collections.synchronizedMap(
         new WeakHashMap<ListDataListener, Object>());
     private final Object present = new Object();
     private ArrayList<Object> delegate = new ArrayList<Object>();

Синхронизация слушателю список WeakHashMap Доступа к своим. Хотя в некоторых реализациях слушателю списков полагаться на изготовление копий, чтобы избежать синхронизированный доступ, слабая ссылки могут быть сняты в любое время. Так что изготовление копии предоставляет два места для слабых ведения, который будет не упал с добавленной стоимостью.

Because the listener list is maintained in a Map, you need both a key and a value. The value associated with every key in the map is the ‘present‘ object. Theoretically, you only need a WeakHashSet. However, the class is not in the predefined set of collections, so a WeakHashMap is used instead.

The last variable, delegate, manages the ListModel contents. Most of the ListModel interface implementation simply passes the request to the delegate to perform the operation, hence the name delegate.

Первый набор методов для WeakListModel относятся к калибровке или запрашивая модель структуры. Во всех случаях эти передать вызов delegate


   public int getSize() {
     return delegate.size();
   }

   public Object getElementAt(int index) {
     return delegate.get(index);
   }

   public void trimToSize() {
     delegate.trimToSize();
   }

   public void ensureCapacity(int minCapacity) {
     delegate.ensureCapacity(minCapacity);
   }

   public int size() {
     return delegate.size();
   }

   public boolean isEmpty() {
     return delegate.isEmpty();
   }

   public Enumeration elements() {
     return Collections.enumeration(delegate);
   }

   public boolean contains(Object elem) {
     return delegate.contains(elem);
   }

   public int indexOf(Object elem) {
     return delegate.indexOf(elem);
   }

   public int lastIndexOf(Object elem) {
     return delegate.lastIndexOf(elem);
   }

   public Object elementAt(int index) {
     return delegate.get(index);
   }

   public Object firstElement() {
     return delegate.get(0);
   }

   public Object lastElement() {
     return delegate.get(delegate.size()-1);
   }

   public String toString() {
     return delegate.toString();
   }

Следующий набор методов имеем дело с добавление и удаление элементов. Помимо доступа к delegate для хранения операции, набор слушателей должны быть уведомлены. Эти методы вызова fire XXX () методы, которые будут показаны в ближайшее время. Эти методы делают основную работу в классе.


   public void setElementAt(Object obj, int index) {
     delegate.set(index, obj);
     fireContentsChanged(this, index, index);
   }

   public void removeElementAt(int index) {
     delegate.remove(index);
     fireIntervalRemoved(this, index, index);
   }

   public void insertElementAt(Object obj, int index) {
     delegate.add(index, obj);
     fireIntervalAdded(this, index, index);
  }
 
   public void addElement(Object obj) {
     int index = delegate.size();
     delegate.add(obj);
     fireIntervalAdded(this, index, index);
   }

   public boolean removeElement(Object obj) {
     int index = indexOf(obj);
     boolean rv = delegate.remove(obj);
     if (index >= 0) {
       fireIntervalRemoved(this, index, index);
     }
     return rv;
   }

   public void removeAllElements() {
     int index1 = delegate.size()-1;
     delegate.clear();
     if (index1 >= 0) {
       fireIntervalRemoved(this, 0, index1);
     }
   }

Слушатель сам список сделок с добавлять и удалять слушателю вызовов. Опять же, present объект не используется сам. "Карту", просто нужен ключ и значение. Это та же модель используют TreeSet которая опирается на TreeMap

   public synchronized void addListDataListener(
    ListDataListener l) {
     listenerList.put(l, present);
   }

   public synchronized void removeListDataListener(
    ListDataListener l) {
     listenerList.remove(l);
   }

   public EventListener[] getListeners(Class listenerType) {
     Set<ListDataListener> set = listenerList.keySet();
     return set.toArray(new EventListener[0]);
   }

Наиболее интересный набор методов fire XXX () методы. Для всех трех из этих методов, fireContentsChanged() fireIntervalAdded() и fireIntervalRemoved() новый Set создан с ключами от WeakHashMap Это делается, чтобы гарантировать, что если установить изменения в середине уведомления, оригинальный Набор еще уведомление.

   protected synchronized void fireContentsChanged(
       Object source, int index0, int index1) {
     ListDataEvent e = null;

     Set<ListDataListener> set =
       new HashSet<ListDataListener>(listenerList.keySet());
     Iterator<ListDataListener> iter = set.iterator();

     while (iter.hasNext()) {
       if (e == null) {
         e = new ListDataEvent(
          source, ListDataEvent.CONTENTS_CHANGED,
          index0, index1);
       }
       ListDataListener ldl = iter.next();
       ldl.contentsChanged(e);
     }
   }

   protected synchronized void fireIntervalAdded(
       Object source, int index0, int index1) {
     ListDataEvent e = null;

     Set<ListDataListener> set =
       new HashSet<ListDataListener>(listenerList.keySet());
     Iterator<ListDataListener> iter = set.iterator();

     while (iter.hasNext()) {
       if (e == null) {
         e = new ListDataEvent(
          source, ListDataEvent.INTERVAL_ADDED,
          index0, index1);
       }
       ListDataListener ldl = iter.next();
       ldl.intervalAdded(e);
     }
   }

   protected synchronized void fireIntervalRemoved(
       Object source, int index0, int index1) {
     ListDataEvent e = null;

     Set<ListDataListener> set =
       new HashSet<ListDataListener>(listenerList.keySet());

     Iterator<ListDataListener> iter = set.iterator();

     while (iter.hasNext()) {
       if (e == null) {
         e = new ListDataEvent(
          source, ListDataEvent.INTERVAL_REMOVED,
          index0, index1);
       }
       ListDataListener ldl = iter.next();
       ldl.intervalRemoved(e);
     }
   }

Добавить импорта и закрыть скобку и у вас есть определение класса:


   import java.io.Serializable;
   import java.util.*;
   import java.lang.ref.*;
   import javax.swing.*;
   import javax.swing.event.*;

   ...

   }

Чтобы проверить ListModel следующая программа, TestListModel определяет ListDataListener и связывает его с созданным WeakListModel Обратите внимание, что добавление и удаление имен основателей ВС в качестве элементов модели уведомляет слушателю. Как только пошел сильный ссылкой на слушателя, WeakListModel может освободить слабых ссылкой слушателю. Дальнейшие операции по модели тогда не уведомит слушателю.


   import javax.swing.*;
   import javax.swing.event.*;

   public class TestListModel {
     public static void main (String args[]) {
       ListDataListener ldl = new ListDataListener() {
         public void intervalAdded(ListDataEvent e) {
           System.out.println("Added: " + e);
         }
         public void intervalRemoved(ListDataEvent e) {
           System.out.println("Removed: " + e);
         }
         public void contentsChanged(ListDataEvent e) {
           System.out.println("Changed: " + e);
         }
       };
       WeakListModel model = new WeakListModel();
       model.addListDataListener(ldl);
       model.addElement("Scott McNealy");
       model.addElement("Bill Joy");
       model.addElement("Andy Bechtolsheim");
       model.addElement("Vinod Khosla");
       model.removeElement("Scott McNealy");
       ldl = null;
       System.gc();
       model.addElement("Scott McNealy");
       System.out.println(model);
     }
   }

Обобщение и запустить программу TestListModel Она должна создать следующие результаты:


 > java TestListModel

Added: javax.swing.event.ListDataEvent[type=1,index0=0,index1
=0]
Added: javax.swing.event.ListDataEvent[type=1,index0=1,index1
=1]
Added: javax.swing.event.ListDataEvent[type=1,index0=2,index1
=2]
Added: javax.swing.event.ListDataEvent[type=1,index0=3,index1
=3]
Removed: javax.swing.event.ListDataEvent[type=2,index0=0,inde
x1=0]
[Bill Joy, Andy Bechtolsheim, Vinod Khosla, Scott McNealy]
See the for additional information about reference objects, notification, and reachability.

Copyright (C) 2004-2005 Sun Microsystems, Inc
Все права защищены.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>