【问题标题】:Limiting the max size of a HashMap in Java在 Java 中限制 HashMap 的最大大小
【发布时间】:2011-08-01 20:40:06
【问题描述】:

我想限制HashMap 的最大大小,以便对我正在实施的各种散列算法进行度量。我查看了HashMap 的重载构造函数之一中的负载因子。

HashMap(int initialCapacity, float loadFactor) 

我尝试在构造函数中将 loadFactor 设置为 0.0f(这意味着我不希望 HashMap 的大小永远增长)但 javac 将此无效:

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

还有其他方法可以限制HashMap 的大小,使其永远不会增长吗?

【问题讨论】:

  • 当 Map 已满并且您尝试插入另一个元素时会发生什么?
  • 仅供参考,哈希表需要压缩其键空间,因为您无法保留 2^31 * 4 字节的内存空间来保存每个可能键的值。因此,哈希表通常会截断哈希并使用链表进行冲突。 loadFactor 粗略地指示在表开始使用更多哈希位之前链接的最大大小。因此,长度为 0 的链表没有意义:您不能在其中存储任何内容。
  • 负载因子表示何时增加数据结构大小。初始大小(i)和负载因子(x)意味着,当我们有 i*x 个元素时,我们会增加大小。如果 x = 0,就像要求 Java 在数据结构有 0 个元素时增加它的大小。

标签: java hashmap


【解决方案1】:

有时越简单越好。

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

    ...
}

【讨论】:

  • 这个答案限制了地图的最大大小。请参阅 Margus 的答案以获取更简单的 Map 以防止放置 或删除 条目。
  • @mattburns,这不是问题吗?还是在您发表评论后问题已被改写?
  • @sriman ,嗯,是的,匹配问题标题,但不匹配详细的问题描述。 OP 希望它永远不会增长(例如,不可变)。但是 10 年后,阅读这篇文章的人可能只会在这里,因为他们搜索的是限制哈希图的最大容量......嗯
【解决方案2】:

您可以像这样创建一个新类来限制 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;
    }
}

【讨论】:

  • 澄清一下,如果我理解正确的话,这样当你插入一个新元素时,它只是从地图中删除最旧的元素,并插入新元素,从而将大小限制为@987654322 @。并不是不让你添加新元素。
【解决方案3】:

HashMap类中的方法put负责将元素添加到HashMap中,它通过调用addEntry方法来实现,代码如下:

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

正如您在此方法中看到的那样,如果超过阈值,HashMap 会调整大小,所以我会尝试扩展 HashMap 类并为putaddEntry 编写我自己的方法,以消除调整大小.比如:

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

您需要编写自己的方法,因为 putaddEntry 不能被覆盖,而且您还需要对 putForNullKey 执行相同的操作,因为它在 put 内部调用。如果表已满,则需要在put 中进行验证,以验证我们没有尝试放置对象。

【讨论】:

    【解决方案4】:

    简单的解决方案通常是最好的,所以使用unmodifiableImmutable hashmap。

    如果你不能改变元素的数量,那么大小将是固定的 - 问题解决了。

    【讨论】:

    • 并不总是更好,因为当内存中有大量数据需要处理时,最常使用 HashMap。使用不可变哈希映射时,内存消耗可能会成为一个问题
    【解决方案5】:
    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;
        }
    
    
    }
    

    【讨论】:

      【解决方案6】:

      我尝试在构造函数中将 loadFactor 设置为 0.0f(这意味着我不希望 HashMap 的大小永远增长)但 javac 认为这是无效的

      loadFactor 为 1 表示“在 HashMap 100% 满之前不要增长”。如果被接受,loadFactor 为 0 意味着“呈指数增长”。

      来自HashMap docs

      容量是哈希表中桶的数量,初始容量就是哈希表创建时的容量。 负载因子 是衡量哈希表在其容量自动增加之前允许达到的程度。当哈希表中的条目数超过负载因子与当前容量的乘积时,对哈希表进行rehashed(即重建内部数据结构),使得哈希表大约有桶数的两倍。

      示例:使用默认设置初始化的 HashMap 的容量为 16,负载因子为 0.75。 Capacity * load factor = 16 * 0.75 = 12。因此,将第 13 项添加到 HashMap 将使其增长到(大约)32 个桶。

      无效示例:使用 16 容量和 0 负载因子初始化的 HashMap。Capacity * load factor = 16 * 0 = 0。因此,每次添加项目的尝试都会触发重新散列和大小翻倍,直到内存不足。

      你最初想要的:

      如果初始容量大于最大条目数除以负载因子,则不会发生重新哈希操作。

      如果你创建一个容量 M > N、负载因子为 1 的 HashMap,并添加 N 个项,它就不会增长。

      【讨论】:

        猜你喜欢
        • 2016-05-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-08
        • 1970-01-01
        • 2016-05-04
        相关资源
        最近更新 更多