Index: lutinutil/src/java/org/codelutin/util/HashMapMultiKey.java diff -u lutinutil/src/java/org/codelutin/util/HashMapMultiKey.java:1.9 lutinutil/src/java/org/codelutin/util/HashMapMultiKey.java:1.10 --- lutinutil/src/java/org/codelutin/util/HashMapMultiKey.java:1.9 Mon May 22 12:40:50 2006 +++ lutinutil/src/java/org/codelutin/util/HashMapMultiKey.java Tue May 23 13:55:52 2006 @@ -23,9 +23,9 @@ * Created: 2 nov. 2004 * * @author Benjamin Poussin - * @version $Revision: 1.9 $ + * @version $Revision: 1.10 $ * - * Mise a jour: $Date: 2006/05/22 12:40:50 $ + * Mise a jour: $Date: 2006/05/23 13:55:52 $ * par : $Author: bpoussin $ */ @@ -37,7 +37,9 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.IdentityHashMap; import java.util.Iterator; +import java.util.Map; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -62,11 +64,11 @@ public static final Object SOFT = new Object(); public static final Object WEAK = new Object(); - /** key: un objet, value une list de key pour la vrai hashmap */ - protected HashMap keys = new HashMap(); + /** key: un objet, value un Set de key pour la vrai hashmap */ + protected HashMap keys = new HashMap(); /** key: la valeur, valeur: la cle. Permet de supprimer rapidement une * key lorsque la valeur disparait */ - protected HashMap valueToKey = new HashMap(); + protected Map valueToKey = new IdentityHashMap(); protected ReferenceQueue refQueueKey = new ReferenceQueue(); protected ReferenceQueue refQueueValue = new ReferenceQueue(); @@ -91,8 +93,57 @@ } /** + * Encapsule dans un objet Reference si besoin l'objet o + */ + protected Object refValue(Object o) { + Object result = o; + if (valueType == SOFT) { + result = new TransparenteSoftReference(o, refQueueValue, false); + } else if (valueType == WEAK) { + result = new TransparenteWeakReference(o, refQueueValue, false); + } + return result; + } + + protected Object derefValue(Object o) { + Object result = o; + if (valueType == SOFT && o != null) { + result = ((TransparenteSoftReference)o).get(); + } else if (valueType == WEAK && o != null) { + result = ((TransparenteWeakReference)o).get(); + } + return result; + } + + + /** + * Encapsule dans un objet Reference si besoin l'objet o + */ + protected Object refKey(Object o){ + Object result = o; + if (keyType == SOFT) { + result = new TransparenteSoftReference(o, refQueueKey, false); + } else if (keyType == WEAK) { + result = new TransparenteWeakReference(o, refQueueKey, false); + } + return result; + } + + protected Object derefKey(Object o) { + Object result = o; + if (keyType == SOFT && o != null) { + result = ((TransparenteSoftReference)o).get(); + } else if (keyType == WEAK && o != null) { + result = ((TransparenteWeakReference)o).get(); + } + return result; + } + + /** * Look in the queue if reference is available and remove reference in * all map + * ATTENTION: this method not call other method, because all other method + * call it */ protected void cleanQueue(){ Reference o = null; @@ -106,71 +157,50 @@ if(log.isTraceEnabled()){ log.trace("clean the refQueueValue : " + o);} // suppression dans la table des values et recuperation de la cle // associé - Object key = derefKey(valueToKey.remove(o)); + Object keyRef = valueToKey.remove(o); + // suppression dans la table a partir de la cle recuperé + super.remove(keyRef); + + Object key = derefKey(keyRef); +// System.out.println("++++++++++ key: " + key + " keyRef: " + keyRef + " for value: " + o); if( key != null) { - // suppression dans la table a partir de la cle recuperé - super.remove(key); // suppression dans l'association des objets de la cle for(Iterator i=((Key)key).iterator(); i.hasNext();){ - Set list = _getKeys(i.next()); + Object k = i.next(); + Set list = (Set)keys.get(k); + // FIXME il arrive que la list soit null, mais cela devrait + // etre impossible puisqu'on vient de recuperer une cle qui + // contient cette sous cle qui devrait retourner cette liste if (list != null) { - list.remove(key); + list.remove(keyRef); + if (list.size() == 0) { + keys.remove(k); + } } } } else { - if(log.isTraceEnabled()){ log.trace("key is null !!! " + o);} + if(log.isTraceEnabled()){ log.trace("key is null !!! do long task to prevent memory leak " + o);} + // to prevent memory leak we can clean all list in keys Map + for (Iterator e=keys.keySet().iterator(); e.hasNext();) { + Object k = e.next(); + Set list = keys.get(k); + if (list != null) { + for (Iterator i=list.iterator(); i.hasNext();) { + if (derefKey(i.next()) == null) { + i.remove(); + } + } + if (list.size() == 0) { + e.remove(); + } + } + } + if(log.isTraceEnabled()){ log.trace("end of long task to prevent memory leak" + o);} } } } /** - * Encapsule dans un objet Reference si besoin l'objet o - */ - protected Object refValue(Object o) { - Object result = o; - if (valueType == SOFT) { - result = new TransparenteSoftReference(o, refQueueValue, false); - } else if (valueType == WEAK) { - result = new TransparenteWeakReference(o, refQueueValue, false); - } - return result; - } - - protected Object derefValue(Object o) { - Object result = o; - if (valueType == SOFT && o != null) { - result = ((TransparenteSoftReference)o).get(); - } else if (valueType == WEAK && o != null) { - result = ((TransparenteWeakReference)o).get(); - } - return result; - } - - - /** - * Encapsule dans un objet Reference si besoin l'objet o - */ - protected Object refKey(Object o){ - Object result = o; - if (keyType == SOFT) { - result = new TransparenteSoftReference(o, refQueueKey, false); - } else if (keyType == WEAK) { - result = new TransparenteWeakReference(o, refQueueKey, false); - } - return result; - } - - protected Object derefKey(Object o) { - Object result = o; - if (keyType == SOFT && o != null) { - result = ((TransparenteSoftReference)o).get(); - } else if (keyType == WEAK && o != null) { - result = ((TransparenteWeakReference)o).get(); - } - return result; - } - - /** * Retourne toutes les cles qui contiennent au moins une fois l'élément * e. * @param e l'element que doivent contenir les cles @@ -179,15 +209,6 @@ */ public Set getKeys(Object e){ Set list = _getKeys(e); - - for (Iterator i=list.iterator(); i.hasNext();) { - if (derefKey(i.next()) == null) { - i.remove(); - } - } - if (list.size() == 0) { - keys.remove(e); - } return list; } @@ -226,6 +247,9 @@ * une collection d'objet. */ public Object put(Object key, Object value){ + if (key == null) { + if(log.isWarnEnabled()) {log.warn("key is null");} + } // a chaque fois que l'on ajoute, on nettoie un peu avant cleanQueue(); if(key instanceof Key){ @@ -247,6 +271,7 @@ throw new IllegalArgumentException("L'argument key doit etre un tableau d'objet"); } + public boolean containsValue(Object value) { cleanQueue(); return valueToKey.containsKey(value); @@ -281,21 +306,26 @@ * des cles qui contient cet objet et qui ont été supprimées. */ public Object remove(Object key){ - // a chaque fois que l'on ajoute, on nettoie un peu avant + // on nettoie un peu avant cleanQueue(); if(key instanceof Key){ // suppression de la cle // on pourrait aussi mettre la cle dans un WeakRef, ca eviterait // de devoir le supprimer dans les lists de keys, le // GC le ferait pour nous. + Object keyRef = refKey(key); for(Iterator i=((Key)key).iterator(); i.hasNext();){ - Set list = _getKeys(i.next()); + Object k = i.next(); + Set list = _getKeys(k); if (list != null) { - list.remove(key); - } + list.remove(keyRef); + if (list.size() == 0) { + keys.remove(k); + } + } } - Object result = super.remove(key); + Object result = super.remove(keyRef); valueToKey.remove(result); return derefValue(result); }else{ @@ -303,11 +333,12 @@ Set list = _getKeys(key); if ( list != null ){ for(Iterator i=list.iterator(); i.hasNext();){ - Key keyObject = (Key)derefKey(i.next()); + Object keyRef = i.next(); + Key keyObject = (Key)derefKey(keyRef); i.remove(); if (keyObject != null) { // si la cle existe encore result.add(keyObject); - Object value = super.remove(keyObject); + Object value = super.remove(keyRef); valueToKey.remove(value); } } Index: lutinutil/src/java/org/codelutin/util/TransparenteSoftReference.java diff -u lutinutil/src/java/org/codelutin/util/TransparenteSoftReference.java:1.5 lutinutil/src/java/org/codelutin/util/TransparenteSoftReference.java:1.6 --- lutinutil/src/java/org/codelutin/util/TransparenteSoftReference.java:1.5 Mon May 22 12:40:50 2006 +++ lutinutil/src/java/org/codelutin/util/TransparenteSoftReference.java Tue May 23 13:55:52 2006 @@ -22,9 +22,9 @@ * * @author Benjamin Poussin * Copyright Code Lutin -* @version $Revision: 1.5 $ +* @version $Revision: 1.6 $ * -* Mise a jour: $Date: 2006/05/22 12:40:50 $ +* Mise a jour: $Date: 2006/05/23 13:55:52 $ * par : $Author: bpoussin $ */ package org.codelutin.util; @@ -99,8 +99,11 @@ o = ((Reference)o).get(); } - return (o == null && local == null) - || (o != null && o.equals(local)); + boolean result = + (o == null && local == null) + || (o != null && o.equals(local)); + + return result; } /** Index: lutinutil/src/java/org/codelutin/util/LRUMapMultiKey.java diff -u /dev/null lutinutil/src/java/org/codelutin/util/LRUMapMultiKey.java:1.1 --- /dev/null Tue May 23 13:55:57 2006 +++ lutinutil/src/java/org/codelutin/util/LRUMapMultiKey.java Tue May 23 13:55:52 2006 @@ -0,0 +1,234 @@ +/* *##% + * Copyright (C) 2006 + * Code Lutin, Cédric Pineau, Benjamin Poussin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +/* * + * LRUMapMultiKey.java + * + * Created: 23 mai 2006 04:08:03 + * + * @author poussin + * @version $Revision: 1.1 $ + * + * Last update: $Date: 2006/05/23 13:55:52 $ + * by : $Author: bpoussin $ + */ + +package org.codelutin.util; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + +/** + * @author poussin + * + */ + +public class LRUMapMultiKey extends LinkedHashMap { + + private static final Log log = LogFactory.getLog(LRUMapMultiKey.class); + + /** + * @author poussin + * + */ + static public class Key extends ArrayList { + +// protected LRUMapMultiKey map = null; +// protected Reference ref = null; + protected int hash = 0; + + public Key(Object ... k) { + Collections.addAll(this, k); + } + + @Override + public int hashCode() { + if (hash == 0) { + hash = super.hashCode(); + } + return hash; + } + +// /* (non-Javadoc) +// * @see java.util.AbstractList#equals(java.lang.Object) +// */ +// @Override +// public boolean equals(Object o) { +// if (o != null && o instanceof Reference) { +// Object ref = ((Reference)o).get(); +// if (ref == null) { +// boolean result = o.hashCode() == hashCode(); +// return result; +// } +// } +// return super.equals(o); +// } + +// /* (non-Javadoc) +// * @see java.lang.Object#finalize() +// */ +// @Override +// protected void finalize() throws Throwable { +// if (map != null) { +// for (Iterator i=iterator(); i.hasNext();) { +// Object k = i.next(); +// Set> list = map.keys.get(k); +// if (list != null) { +// Object o = ref; +// if (o == null) { +// o = this; +// } +// boolean ok = list.remove(o); +// if (list.size() == 0) { +// map.keys.remove(k); +// } +// } +// } +// } +// } + + } + + + protected Map> keys = new HashMap>(); + protected int maxSize; + + public LRUMapMultiKey(int maxSize) { + super(maxSize<=0?1000:maxSize*100/75, (float)0.75, true); + this.maxSize = maxSize; + } + + public Key createKey(Object ... k) { + return new Key(k); + } + + /* (non-Javadoc) + * @see java.util.WeakHashMap#clear() + */ + @Override + public void clear() { + keys.clear(); + super.clear(); + } + + /* (non-Javadoc) + * @see java.util.WeakHashMap#remove(java.lang.Object) + */ + @Override + public Object remove(Object k) { + if (k instanceof Key) { + return super.remove(k); + } else { + ArrayList result = new ArrayList(); + Set list = keys.remove(k); + if (list != null) { + for (Iterator i=list.iterator(); i.hasNext();){ + Key key = i.next(); + result.add(key); + super.remove(key); + } + list.clear(); // not necessary but perhaps help the garbage + } + return result; + } + } + + /* (non-Javadoc) + * @see java.util.WeakHashMap#put(java.lang.Object, java.lang.Object) + */ + @Override + public Object put(Key key, Object value) { +// if (!(akey instanceof Key)) { +// throw new IllegalArgumentException("key must be Key object"); +// } +// Key key = (Key)akey; + for (Iterator i=key.iterator();i.hasNext();) { + Object k = i.next(); + Set list = keys.get(k); + if (list == null) { + list = new HashSet(); + keys.put(k, list); + } + list.add(key); +//System.out.println("+++++++++++++++++++ put key: " + key + " list("+k+") == " + list.size()); + } +//System.out.println("++++++++++++++++++++++++++++ LRU size = " + size() + " maxSize: " + maxSize); + Object result = super.put(key, value); +//System.out.println("+++++++++++++++++ LRU size = " + size()); + return result; + } + + /* (non-Javadoc) + * @see java.util.LinkedHashMap#removeEldestEntry(java.util.Map.Entry) + */ + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + if (this.maxSize > 0 && size() > this.maxSize) { + Key key = (Key)eldest.getKey(); + for (Iterator i=key.iterator(); i.hasNext();) { + Object k = i.next(); + Set list = keys.get(k); + if (list != null) { + boolean ok = list.remove(key); + if (list.size() == 0) { + keys.remove(k); + } + } + } + + if (!containsKey(eldest.getKey())) { + log.warn("possible memory leak !!! removeEldestEntry ("+eldest.getKey().getClass()+")" + eldest.getKey() + " size " + size() + " maxSize" + maxSize) ; + } + return true; + } + return false; + } + +// /* (non-Javadoc) +// * @see org.apache.commons.collections.map.LRUMap#removeLRU(org.apache.commons.collections.map.AbstractLinkedMap.LinkEntry) +// */ +// @Override +// protected boolean removeLRU(AbstractLinkedMap.LinkEntry entry) { +// Key key = (Key)entry.getKey(); +// for (Iterator i=key.iterator(); i.hasNext();) { +// Object k = i.next(); +// Set list = keys.get(k); +// if (list != null) { +// boolean ok = list.remove(key); +// if (list.size() == 0) { +// keys.remove(k); +// } +// } +// } +// return true; +// } + +} + +