Biojava guys (/girls?) , I am using biojava for sequence alignment stats. I ran into a really bad performance problem, which was apparently was caused by a rather naive implementation of org.biojava.util.ChangeSupport class. Rewriting that class sped up my application by more then factor of 10. I am sending you a copy of my implementation. I haven't changed any of the methods (although some constructors are rather meaningless now), so you should be able to just swap it in.
-peter.
/* * BioJava development code * * This code may be freely distributed and modified under the * terms of the GNU Lesser General Public Licence. This should * be distributed with the code. If you do not have a copy, * see: * * http://www.gnu.org/copyleft/lesser.html * * Copyright for this code is held jointly by the individual * authors. These should be listed in @author doc comments. * * For more information on the BioJava project and its aims, * or to join the biojava-l mailing list, visit the home page * at: * * http://www.biojava.org/ */ package org.biojava.utils; import java.io.Serializable; import java.util.*; import java.lang.ref.*; /** * A utility class to provide management for informing ChangeListeners of * ChangeEvents. * <P> * This is loosely modelled after the standard PropertyChangeEvent objects. * <P> * For an object to correctly fire these events, they must follow a broad * outline like this: * <code><pre> * public void mutator(foo arg) throw ChangeVetoException { * ChangeEvent cevt = new ChangeEvent(this, SOME_EVENT_TYPE, arg); * synchronized(changeSupport) { * changeSupport.firePreChangeEvent(cevt); * // update our state using arg * // ... * changeSupport.firePostChangeEvent(cevt); * } * } * </pre></code> * The methods that delegate adding and removing listeners to a ChangeSupport * must take responsibility for synchronizing on the delegate. * * @author Matthew Pocock * @author Thomas Down * @author Keith James (docs) * @since 1.1 */ public class ChangeSupport { private Hashtable changeTypes; /** * Generate a new ChangeSupport instance. */ public ChangeSupport() { changeTypes=new Hashtable(); } /** * Generate a new ChangeSupport instance which has room for initialSize * listeners before it needs to grow any resources. * * @param initialSize the number of listeners that can be added before this * needs to grow for the first time */ public ChangeSupport(int initialSize) { this(); } /** * Generate a new ChangeSupport instance which has room for initialSize * listeners before it needs to grow any resources, and which will grow by * delta each time. * * @param initialSize the number of listeners that can be added before this * needs to grow for the first time * @param delta the number of listener slots that this will grow by each time * it needs to */ public ChangeSupport(int initialSize, int delta) { this(); } /** * Add a listener that will be informed of all changes. * * @param cl the ChangeListener to add */ public void addChangeListener(ChangeListener cl) { addChangeListener(cl, ChangeType.UNKNOWN); } /** * Add a listener that will be informed of changes of a given type (and it's subtypes) * * @param cl the ChangeListener * @param ct the ChangeType it is to be informed of */ public synchronized void addChangeListener(ChangeListener cl, ChangeType ct) { // Needed to synchronize this method in case multiple threads attempt to add a change listener at the same time. // Richard J. Fox 05/30/2001 if (ct == null) { throw new NestedError("Since 1.2, listeners registered for the null changetype are not meaningful. Please register a listener for ChangeType.UNKNOWN instead"); } else { ChangeTypeListeners ctl=(ChangeTypeListeners)changeTypes.get(ct); // if this type has not been registered before if(ctl==null) { ctl=new ChangeTypeListeners(ct); changeTypes.put(ct,ctl); } } } /** * Remove a listener that was interested in all types of changes. * * @param cl a ChangeListener to remove */ public void removeChangeListener(ChangeListener cl) { removeChangeListener(cl, ChangeType.UNKNOWN); } /** * Remove a listener that was interested in a specific types of changes. * * @param cl a ChangeListener to remove * @param ct the ChangeType that it was interested in */ public void removeChangeListener(ChangeListener cl, ChangeType ct) { if(ct==null) { throw new NestedError("Since 1.2, listeners registered for the null changetype are not meaningful. Please register a listener for ChangeType.UNKNOWN instead"); } else { ChangeTypeListeners ctl=(ChangeTypeListeners)changeTypes.get(ct); if(ct!=null) { ctl.listeners.remove(cl); } } } /** * Inform the listeners that a change is about to take place using their * firePreChangeEvent methods. * <P> * Listeners will be informed if they were interested in all types of event, * or if ce.getType() is equal to the type they are registered for. * * @param ce the ChangeEvent to pass on * @throws ChangeVetoException if any of the listeners veto this change */ public void firePreChangeEvent(ChangeEvent ce) throws ChangeVetoException { ChangeType ct = ce.getType(); if(ct!=null) { ChangeTypeListeners ctl=(ChangeTypeListeners)changeTypes.get(ct); if(ctl!=null) { for(Iterator i=ctl.listeners.iterator();i.hasNext();) { ChangeListener cl=(ChangeListener)i.next(); cl.preChange(ce); } } } } /** * Inform the listeners that a change has taken place using their * firePostChangeEvent methods. * <P> * Listeners will be informed if they were interested in all types of event, * or if ce.getType() is equal to the type they are registered for. * * @param ce the ChangeEvent to pass on */ public void firePostChangeEvent(ChangeEvent ce) { ChangeType ct = ce.getType(); if(ct!=null) { ChangeTypeListeners ctl=(ChangeTypeListeners)changeTypes.get(ct); if(ctl!=null) { for(Iterator i=ctl.listeners.iterator();i.hasNext();) { ChangeListener cl=(ChangeListener)i.next(); cl.postChange(ce); } } } } /** A little private class to hold a change type together with a set of listeners for that type */ private class ChangeTypeListeners { Set listeners; ChangeType type; public ChangeTypeListeners(ChangeType t) { this.type=t; this.listeners=Collections.synchronizedSet(new HashSet()); } } }
