Je souhaite limiter la taille maximale d'un HashMap
pour prendre des mesures sur une variété d'algorithmes de hachage que j'implémente. J'ai regardé le facteur de charge dans l'un des constructeurs surchargés de HashMap
.
HashMap(int initialCapacity, float loadFactor)
J'ai essayé de définir le loadFactor à 0.0f dans le constructeur (ce qui signifie que je ne veux pas que le HashMap grandisse) mais javac
appelle cela invalide:
Exception in thread "main" Java.lang.IllegalArgumentException: Illegal load factor: 0.0
at Java.util.HashMap.<init>(HashMap.Java:177)
at hashtables.CustomHash.<init>(Main.Java:20)
at hashtables.Main.main(Main.Java:70) Java Result: 1
Existe-t-il un autre moyen de limiter la taille de HashMap
pour qu'elle ne croisse jamais?
Parfois plus simple, c'est mieux.
public class InstrumentedHashMap<K, V> implements Map<K, V> {
private Map<K, V> map;
public InstrumentedHashMap() {
map = new HashMap<K, V>();
}
public boolean put(K key, V value) {
if (map.size() >= MAX && !map.containsKey(key)) {
return false;
} else {
map.put(key, value);
return true;
}
}
...
}
Vous pouvez créer une nouvelle classe comme celle-ci pour limiter la taille d'un HashMap:
public class MaxSizeHashMap<K, V> extends LinkedHashMap<K, V> {
private final int maxSize;
public MaxSizeHashMap(int maxSize) {
this.maxSize = maxSize;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > maxSize;
}
}
Une solution simple est généralement la meilleure, alors utilisez non modifiable ou immuable hashmap.
Si vous ne pouvez pas modifier la quantité d'éléments, la taille sera corrigée - problème résolu.
public class Cache {
private LinkedHashMap<String, String> Cache = null;
private final int cacheSize;
private ReadWriteLock readWriteLock=null;
public Cache(LinkedHashMap<String, String> psCacheMap, int size) {
this.Cache = psCacheMap;
cacheSize = size;
readWriteLock=new ReentrantReadWriteLock();
}
public void put(String sql, String pstmt) throws SQLException{
if(Cache.size() >= cacheSize && cacheSize > 0){
String oldStmt=null;
String oldSql = Cache.keySet().iterator().next();
oldStmt = remove(oldSql);
oldStmt.inCache(false);
oldStmt.close();
}
Cache.put(sql, pstmt);
}
public String get(String sql){
Lock readLock=readWriteLock.readLock();
try{
readLock.lock();
return Cache.get(sql);
}finally{
readLock.unlock();
}
}
public boolean containsKey(String sql){
Lock readLock=readWriteLock.readLock();
try{
readLock.lock();
return Cache.containsKey(sql);
}finally{
readLock.unlock();
}
}
public String remove(String key){
Lock writeLock=readWriteLock.writeLock();
try{
writeLock.lock();
return Cache.remove(key);
}finally{
writeLock.unlock();
}
}
public LinkedHashMap<String, String> getCache() {
return Cache;
}
public void setCache(
LinkedHashMap<String, String> Cache) {
this.Cache = Cache;
}
}
La méthode put
de la classe HashMap est celle en charge de l'ajout des éléments dans le HashMap et elle le fait en appelant une méthode nommée addEntry dont le code est le suivant:
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
if (size++ >= threshold)
resize(2 * table.length);
}
Comme vous pouvez le voir dans cette méthode, c'est là que le HashMap est redimensionné si le seuil a été dépassé, donc j'essayerais d'étendre la classe HashMap et d'écrire mes propres méthodes pour put
et addEntry
afin de supprimer le redimensionnement. Quelque chose comme:
package Java.util;
public class MyHashMap<K, V> extends HashMap {
private V myPutForNullKey(V value) {
for (Entry<K, V> e = table[0]; e != null; e = e.next) {
if (e.key == null) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
myAddEntry(0, null, value, 0);
return null;
}
public V myPut(K key, V value) {
if (key == null)
return myPutForNullKey(value);
if (size < table.length) {
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K, V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
myAddEntry(hash, key, value, i);
}
return null;
}
void myAddEntry(int hash, K key, V value, int bucketIndex) {
Entry<K, V> e = table[bucketIndex];
table[bucketIndex] = new Entry<K, V>(hash, key, value, e);
size++;
}
}
Vous auriez besoin d'écrire vos propres méthodes car put
et addEntry
ne peuvent pas être redéfinies et vous devriez également faire de même pour putForNullKey
car elle est appelée à l'intérieur put
. Une validation dans put
est requise pour vérifier que nous n'essayons pas de mettre un objet si la table est pleine.