net.java.ao.SoftHashMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of activeobjects Show documentation
Show all versions of activeobjects Show documentation
This is the full Active Objects library, if you don't know which one to use, you probably want this one.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.java.ao;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.AbstractCollection;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
/**
* Implementation adapted from Apache Harmony
*/
@SuppressWarnings("unchecked")
class SoftHashMap extends AbstractMap implements Map {
private static final int DEFAULT_SIZE = 16;
private Set keySet;
private Collection valuesCollection;
private final ReferenceQueue referenceQueue;
int elementCount;
Entry[] elementData;
private final int loadFactor;
private int threshold;
volatile int modCount;
// Simple utility method to isolate unchecked cast for array creation
private static Entry[] newEntryArray(int size) {
return new Entry[size];
}
private static final class Entry extends SoftReference implements Map.Entry {
int hash;
boolean isNull;
V value;
Entry next;
interface Type {
R get(Map.Entry entry);
}
Entry(K key, V object, ReferenceQueue queue) {
super(key, queue);
hash = key == null ? 0 : key.hashCode();
value = object;
}
public K getKey() {
return super.get();
}
public V getValue() {
return value;
}
public V setValue(V object) {
V result = value;
value = object;
return result;
}
@Override
public boolean equals(Object other) {
if (!(other instanceof Map.Entry)) {
return false;
}
Map.Entry, ?> entry = (Map.Entry, ?>) other;
Object key = super.get();
return (key == null ? key == entry.getKey() : key.equals(entry.getKey())) && (value == null ? value == entry.getValue() : value.equals(entry.getValue()));
}
@Override
public int hashCode() {
return hash + (value == null ? 0 : value.hashCode());
}
@Override
public String toString() {
return super.get() + "=" + value; //$NON-NLS-1$
}
}
class HashIterator implements Iterator {
private int position = 0, expectedModCount;
private Entry currentEntry, nextEntry;
private K nextKey;
final Entry.Type type;
HashIterator(Entry.Type type) {
this.type = type;
expectedModCount = modCount;
}
public boolean hasNext() {
if (nextEntry != null && (nextKey != null || nextEntry.isNull)) {
return true;
}
while (true) {
if (nextEntry == null) {
while (position < elementData.length) {
if ((nextEntry = elementData[position++]) != null) {
break;
}
}
if (nextEntry == null) {
return false;
}
}
// ensure key of next entry is not gc'ed
nextKey = nextEntry.get();
if (nextKey != null || nextEntry.isNull) {
return true;
}
nextEntry = nextEntry.next;
}
}
public R next() {
if (expectedModCount == modCount) {
if (hasNext()) {
currentEntry = nextEntry;
nextEntry = currentEntry.next;
R result = type.get(currentEntry);
// free the key
nextKey = null;
return result;
}
throw new NoSuchElementException();
}
throw new ConcurrentModificationException();
}
public void remove() {
if (expectedModCount == modCount) {
if (currentEntry != null) {
removeEntry(currentEntry);
currentEntry = null;
expectedModCount++;
// cannot poll() as that would change the expectedModCount
} else {
throw new IllegalStateException();
}
} else {
throw new ConcurrentModificationException();
}
}
}
public SoftHashMap() {
this(DEFAULT_SIZE);
}
public SoftHashMap(int capacity) {
if (capacity >= 0) {
elementCount = 0;
elementData = newEntryArray(capacity == 0 ? 1 : capacity);
loadFactor = 7500; // Default load factor of 0.75
computeMaxSize();
referenceQueue = new ReferenceQueue();
} else {
throw new IllegalArgumentException();
}
}
public SoftHashMap(int capacity, float loadFactor) {
if (capacity >= 0 && loadFactor > 0) {
elementCount = 0;
elementData = newEntryArray(capacity == 0 ? 1 : capacity);
this.loadFactor = (int) (loadFactor * 10000);
computeMaxSize();
referenceQueue = new ReferenceQueue();
} else {
throw new IllegalArgumentException();
}
}
public SoftHashMap(Map extends K, ? extends V> map) {
this(map.size() < 6 ? 11 : map.size() * 2);
putAllImpl(map);
}
@Override
public void clear() {
if (elementCount > 0) {
elementCount = 0;
Arrays.fill(elementData, null);
modCount++;
while (referenceQueue.poll() != null) {
// do nothing
}
}
}
private void computeMaxSize() {
threshold = (int) ((long) elementData.length * loadFactor / 10000);
}
@Override
public boolean containsKey(Object key) {
return getEntry(key) != null;
}
@Override
public Set> entrySet() {
poll();
return new AbstractSet>() {
@Override
public int size() {
return SoftHashMap.this.size();
}
@Override
public void clear() {
SoftHashMap.this.clear();
}
@Override
public boolean remove(Object object) {
if (contains(object)) {
SoftHashMap.this.remove(((Map.Entry, ?>) object).getKey());
return true;
}
return false;
}
@Override
public boolean contains(Object object) {
if (object instanceof Map.Entry) {
Entry, ?> entry = getEntry(((Map.Entry, ?>) object).getKey());
if (entry != null) {
Object key = entry.get();
if (key != null || entry.isNull) {
return object.equals(entry);
}
}
}
return false;
}
@Override
public Iterator> iterator() {
return new HashIterator>(new Entry.Type, K, V>() {
public Map.Entry get(Map.Entry entry) {
return entry;
}
});
}
};
}
@Override
public Set keySet() {
poll();
if (keySet == null) {
keySet = new AbstractSet() {
@Override
public boolean contains(Object object) {
return containsKey(object);
}
@Override
public int size() {
return SoftHashMap.this.size();
}
@Override
public void clear() {
SoftHashMap.this.clear();
}
@Override
public boolean remove(Object key) {
if (containsKey(key)) {
SoftHashMap.this.remove(key);
return true;
}
return false;
}
@Override
public Iterator iterator() {
return new HashIterator(new Entry.Type() {
public K get(Map.Entry entry) {
return entry.getKey();
}
});
}
@Override
public Object[] toArray() {
Collection coll = new ArrayList(size());
for (Iterator iter = iterator(); iter.hasNext();) {
coll.add(iter.next());
}
return coll.toArray();
}
@Override
public T[] toArray(T[] contents) {
Collection coll = new ArrayList(size());
for (Iterator iter = iterator(); iter.hasNext();) {
coll.add(iter.next());
}
return coll.toArray(contents);
}
};
}
return keySet;
}
@Override
public Collection values() {
poll();
if (valuesCollection == null) {
valuesCollection = new AbstractCollection() {
@Override
public int size() {
return SoftHashMap.this.size();
}
@Override
public void clear() {
SoftHashMap.this.clear();
}
@Override
public boolean contains(Object object) {
return containsValue(object);
}
@Override
public Iterator iterator() {
return new HashIterator(new Entry.Type() {
public V get(Map.Entry entry) {
return entry.getValue();
}
});
}
};
}
return valuesCollection;
}
@Override
public V get(Object key) {
poll();
if (key != null) {
int index = (key.hashCode() & 0x7FFFFFFF) % elementData.length;
Entry entry = elementData[index];
while (entry != null) {
if (key.equals(entry.get())) {
return entry.value;
}
entry = entry.next;
}
return null;
}
Entry entry = elementData[0];
while (entry != null) {
if (entry.isNull) {
return entry.value;
}
entry = entry.next;
}
return null;
}
Entry getEntry(Object key) {
poll();
if (key != null) {
int index = (key.hashCode() & 0x7FFFFFFF) % elementData.length;
Entry entry = elementData[index];
while (entry != null) {
if (key.equals(entry.get())) {
return entry;
}
entry = entry.next;
}
return null;
}
Entry entry = elementData[0];
while (entry != null) {
if (entry.isNull) {
return entry;
}
entry = entry.next;
}
return null;
}
@Override
public boolean containsValue(Object value) {
poll();
if (value != null) {
for (int i = elementData.length; --i >= 0;) {
Entry entry = elementData[i];
while (entry != null) {
K key = entry.get();
if ((key != null || entry.isNull) && value.equals(entry.value)) {
return true;
}
entry = entry.next;
}
}
} else {
for (int i = elementData.length; --i >= 0;) {
Entry entry = elementData[i];
while (entry != null) {
K key = entry.get();
if ((key != null || entry.isNull) && entry.value == null) {
return true;
}
entry = entry.next;
}
}
}
return false;
}
@Override
public boolean isEmpty() {
return size() == 0;
}
void poll() {
Entry toRemove;
while ((toRemove = (Entry) referenceQueue.poll()) != null) {
removeEntry(toRemove);
}
}
void removeEntry(Entry toRemove) {
Entry entry, last = null;
int index = (toRemove.hash & 0x7FFFFFFF) % elementData.length;
entry = elementData[index];
// Ignore queued entries which cannot be found, the user could
// have removed them before they were queued, i.e. using clear()
while (entry != null) {
if (toRemove == entry) {
modCount++;
if (last == null) {
elementData[index] = entry.next;
} else {
last.next = entry.next;
}
elementCount--;
break;
}
last = entry;
entry = entry.next;
}
}
@Override
public V put(K key, V value) {
poll();
int index = 0;
Entry entry;
if (key != null) {
index = (key.hashCode() & 0x7FFFFFFF) % elementData.length;
entry = elementData[index];
while (entry != null && !key.equals(entry.get())) {
entry = entry.next;
}
} else {
entry = elementData[0];
while (entry != null && !entry.isNull) {
entry = entry.next;
}
}
if (entry == null) {
modCount++;
if (++elementCount > threshold) {
rehash();
index = key == null ? 0 : (key.hashCode() & 0x7FFFFFFF) % elementData.length;
}
entry = new Entry(key, value, referenceQueue);
entry.next = elementData[index];
elementData[index] = entry;
return null;
}
V result = entry.value;
entry.value = value;
return result;
}
private void rehash() {
int length = elementData.length << 1;
if (length == 0) {
length = 1;
}
Entry[] newData = newEntryArray(length);
for (int i = 0; i < elementData.length; i++) {
Entry entry = elementData[i];
while (entry != null) {
int index = entry.isNull ? 0 : (entry.hash & 0x7FFFFFFF) % length;
Entry next = entry.next;
entry.next = newData[index];
newData[index] = entry;
entry = next;
}
}
elementData = newData;
computeMaxSize();
}
@Override
public void putAll(Map extends K, ? extends V> map) {
putAllImpl(map);
}
@Override
public V remove(Object key) {
poll();
int index = 0;
Entry entry, last = null;
if (key != null) {
index = (key.hashCode() & 0x7FFFFFFF) % elementData.length;
entry = elementData[index];
while (entry != null && !key.equals(entry.get())) {
last = entry;
entry = entry.next;
}
} else {
entry = elementData[0];
while (entry != null && !entry.isNull) {
last = entry;
entry = entry.next;
}
}
if (entry != null) {
modCount++;
if (last == null) {
elementData[index] = entry.next;
} else {
last.next = entry.next;
}
elementCount--;
return entry.value;
}
return null;
}
@Override
public int size() {
poll();
return elementCount;
}
private void putAllImpl(Map extends K, ? extends V> map) {
if (map.entrySet() != null) {
super.putAll(map);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy