2011-04-08 48 views
23

quiero limitar el tamaño máximo de un HashMap para tomar mediciones en una variedad de algoritmos de hash que estoy poniendo en práctica. Miré el factor de carga en uno de los constructores sobrecargados de HashMap.Limitar el tamaño máximo de un HashMap en Java

HashMap(int initialCapacity, float loadFactor) 

He intentado establecer el loadFactor a 0.0f en el constructor (es decir, que no quiero la HashMap para crecer en tamaño cada vez), pero este no válidos javac llamadas:

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 

¿Hay otra manera de limitar el tamaño de HashMap para que no crezca nunca?

+8

¿Qué sucede cuando el mapa está lleno e intenta insertar otro elemento? – biziclop

+0

Al igual que un FYI, las tablas hash necesitan comprimir su espacio de claves porque no puede reservar 2^31 * 4 bytes de espacio de memoria para mantener el valor para cada clave posible. Por lo tanto, las tablas hash usualmente truncan el hash y usan listas enlazadas para colisiones. El loadFactor muestra el tamaño máximo del enlace antes de que la tabla comience a usar más bits del hash. Por lo tanto, las listas de 0 longitudes no tienen sentido: no se puede almacenar nada en él. – chacham15

Respuesta

30

A veces simple es mejor.

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(...); 
      return true; 
     } 
    } 

    ... 
} 
+0

@Ishtar, gracias por eso. – Mike

+0

Esta respuesta limita el tamaño máximo de su mapa. Ver la respuesta de Margus para un mapa más simple que evita poner o eliminar entradas. –

89

Se puede crear una nueva clase como esta para limitar el tamaño de 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; 
    } 
} 
+11

+1 por mencionar la clase LinkedHashMap, eliminarEldestEntry() y su función * built in * para mantener este tipo de mapa. Ver: http://docs.oracle.com/javase/1.4.2/docs/api/java/util/LinkedHashMap.html#removeEldestEntry(java.util.Map.Entry) –

+0

de este gran uno! –

+3

java anterior vínculo roto - http://docs.oracle.com/javase/7/docs/api/java/util/LinkedHashMap.html#removeEldestEntry(java.util.Map.Entry) –

1

El método put en la clase HashMap es el encargado de la adición de los elementos en el HashMap y lo hace llamando a un método llamado addEntry qué código es el siguiente:

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); 
    } 

como se puede ver en este método es que el HashMap se cambia el tamaño si el umbral tiene abeja n superado, así que intentaría extender el HashMap clase y escribir mis propios métodos para putaddEntry y con el fin de eliminar el cambio de tamaño. Algo así como:

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++; 
    } 
} 

Usted tendría que escribir sus propios métodos ya putaddEntry y no puede ser de primer orden y también se tendría que hacer lo mismo para putForNullKey ya que se llama en el interior put. Se requiere una validación en put para verificar que no estamos tratando de poner un objeto si la tabla está llena.

6

La solución simple suele ser la mejor, así que use unmodifiable o Immutable hashmap.

Si no puede cambiar la cantidad de elementos, entonces el tamaño se fijará - problema resuelto.

+0

hecho, me gusta esto mucho mejor. – Mike

+0

No siempre es mejor ya que los HashMaps se usan con más frecuencia cuando hay una gran cantidad de datos que se deben manejar en la memoria. El consumo de memoria puede convertirse en un problema cuando se utilizan hashmaps inmutables –

2
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; 
    } 


} 
Cuestiones relacionadas