/************************************************************************** * Parts copyright (c) 2001 by Punch Telematix. All rights reserved. * * Parts copyright (c) 2011 by /k/ Embedded Java Solutions. * * All rights reserved. * * * * Redistribution and use in source and binary forms, with or without * * modification, are permitted provided that the following conditions * * are met: * * 1. Redistributions of source code must retain the above copyright * * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * * notice, this list of conditions and the following disclaimer in the * * documentation and/or other materials provided with the distribution. * * 3. Neither the name of Punch Telematix or of /k/ Embedded Java Solutions* * nor the names of other contributors may be used to endorse or promote* * products derived from this software without specific prior written * * permission. * * * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * * IN NO EVENT SHALL PUNCH TELEMATIX, /K/ EMBEDDED JAVA SOLUTIONS OR OTHER * * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * **************************************************************************/ package java.util; import java.lang.ref.*; public class WeakHashMap extends AbstractMap implements Map { final static Object nullKey = new Object(); private final static float DEFAULT_LOADFACTOR = (float).75; private final static int DEFAULT_CAPACITY = 16; WeakHashReference[] entries; private ReferenceQueue queue; private int threshold; private int capacity; private float loadFactor; int occupancy; int modCount=0; public WeakHashMap() { this(DEFAULT_CAPACITY, DEFAULT_LOADFACTOR); } public WeakHashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOADFACTOR); } public WeakHashMap(int initialCapacity, float loadFactor) { if ( initialCapacity < 0 || loadFactor <= 0.0f ) { throw new IllegalArgumentException("HashMap needs positive numbers"); } this.capacity = (initialCapacity < 5 ? 5 :initialCapacity); entries = new WeakHashReference[capacity]; this.loadFactor = (loadFactor > 1.0f ? DEFAULT_LOADFACTOR : loadFactor ); this.queue = new ReferenceQueue(); } public WeakHashMap(Map map) { int initialCapacity = ((map.size() * 4) / 3) + 5; this.capacity = initialCapacity; entries = new WeakHashReference[capacity]; this.loadFactor = DEFAULT_LOADFACTOR; this.queue = new ReferenceQueue(); Iterator it = map.entrySet().iterator(); try { do { Map.Entry me = (Map.Entry)it.next(); put(me.getKey(), me.getValue()); } while(true); } catch(NoSuchElementException nsee){} } public void clear() { if (occupancy > 0) { Arrays.fill(entries, null); occupancy = 0; modCount++; } } public boolean containsKey(Object key) { checkQueue(); if(key == null) { key = nullKey; } WeakHashReference[] local_entries = (WeakHashReference[]) entries.clone(); int local_capacity = local_entries.length; int hash = key.hashCode() % local_capacity; do { if(hash < 0){ hash += local_capacity; } WeakHashReference entry = local_entries[hash]; if(entry == null) { return false; } else if(key.equals(entry.get())) { return true; } else hash--; } while(true); } public Set entrySet(){ checkQueue(); return new WeakHashMapSet(); } public Object get(Object key) { checkQueue(); if(key==null) { key = nullKey; } int cap = capacity;; int hash = key.hashCode() % cap; do { if(hash < 0){ hash += cap; } WeakHashReference entry = entries[hash]; if(entry == null) { return null; } else if(key.equals(entry.get())) { return entry.value; } else hash--; } while(true); } public boolean isEmpty() { checkQueue(); return (this.occupancy == 0); } public Object put(Object key, Object newvalue) { if(key==null) { key = nullKey; } checkQueue(); int cap = capacity;; int hash = key.hashCode() % cap; do { if(hash < 0){ hash += cap; } WeakHashReference entry = entries[hash]; if(entry == null) { entries[hash] = new WeakHashReference(key, newvalue, queue); int occ = this.occupancy; this.occupancy = ++occ; if(threshold <= occ){ resize(); } modCount++; return null; } else if(key.equals(entry.get())) { Object oldvalue = entry.value; entry.value = newvalue; modCount++; return oldvalue; } hash--; } while(true); } public Object remove(Object key) { checkQueue(); if(key==null) { key = nullKey; } int cap = capacity;; int hash = key.hashCode() % cap; do { if(hash < 0){ hash += cap; } WeakHashReference entry = entries[hash]; if(entry == null) { return null; } else { if(key.equals(entry.get())) { Object oldvalue = entry.value; deleteSlot(hash); modCount++; return oldvalue; } } hash--; } while(true); } public int size() { checkQueue(); return this.occupancy; } void checkQueue(){ WeakHashReference ref = (WeakHashReference) queue.poll(); int cap = capacity; while(ref != null){ int place = ref.hashValue % cap; if(place < 0){ place += cap; } while(ref != entries[place]){ if(--place < 0){ place += cap; } } deleteSlot(place); ref = (WeakHashReference) queue.poll(); } } int firstBusySlot(int i) { int j; if (i<0 || i>=capacity) { return -1; } for(j=i;j<capacity;++j) if (entries[j] != null) { return j; } return -1; } void deleteSlot(int slotIndex) { int vacant, current, home, distance1, distance2; boolean happy; --occupancy; current = slotIndex; while(true) { // R1 entries[current] = null; vacant = current; // R2 happy = true; while(happy) { current = current==0 ? capacity-1 : current-1; // R3 if (entries[current] == null) { return; // normal termination } home = entries[current].hashValue % capacity; if(home < 0){ home += capacity; } distance1 = current<=home ? home - current : capacity + home - current; distance2 = current<=vacant ? vacant - current : capacity + vacant - current; happy = distance1<distance2; // back to R2 } // R4 entries[vacant] = entries[current]; // repeat from R1 ... } } private void resize() { int oldsize = this.capacity; int newsize = oldsize * 2 + 1; WeakHashReference[] oldkeys = entries; WeakHashReference[] entries = new WeakHashReference[newsize]; this.capacity = newsize; this.threshold = (int)(newsize*loadFactor); for (int oldindex=0; oldindex < oldsize; ++oldindex) { WeakHashReference key = oldkeys[oldindex]; if (key != null){ int place = key.hashValue % newsize; if(place < 0){ place += newsize; } while(entries[place] != null){ if(--place < 0){ place += newsize; } } entries[place] = key; } } this.entries = entries; } //inner classes ... class WeakHashMapSet extends AbstractSet { public Iterator iterator() { return new WeakHashMapIterator(); } public int size() { return WeakHashMap.this.size(); } } class WeakHashMapIterator implements Iterator { int pos=firstBusySlot(0); //points to the next free slot int mc=modCount; Object refKey; public boolean hasNext() { return pos !=-1; } public Object next() { if (modCount != mc) { throw new ConcurrentModificationException("don't change the set please (this.modCount == "+mc+", outer.modCount == "+modCount+")"); } if (pos == -1) { throw new NoSuchElementException("no elements left"); } WeakHashReference entry = entries[pos]; refKey = entry.get(); pos = firstBusySlot(pos+1); return entry; } public void remove() { if (refKey == null ) { throw new IllegalStateException("do a next() operation before remove()"); } if (modCount != mc) { throw new ConcurrentModificationException("don't change the set please"); } WeakHashMap.this.remove(refKey); mc++; refKey = null; } } }