繁体   English   中英

紧凑的java.util.Map实现

[英]compact java.util.Map implementation

是否有任何java.util.Map实现不存储等于ValueObjects的值而仅存储其中之一? (此对象的引用不同,但是相等)

这是一些测试代码:

Map<Integer, Integer> map = new HashMap<Integer, Integer>(); 
for (int i = 0; i <= 1000000; i++) { 
map.put(i, new Integer(42)); 
}

和可视VM显示:这些对象的200万个Integer实例相等,但Map存储了所有实例

在此处输入图片说明

仅出于历史记录的目的,我编码了chiastic-security的解决方案并对其进行测试以与HashMap进行比较:

public class CompactMap<K, V> implements Map<K, V> {

private Map<K, V> map;
private Map<V, V> canonicalMap;

public CompactMap() {
    map = new HashMap<K, V>();
    canonicalMap = new HashMap<V, V>();
}

@Override
public void clear() {
    map.clear();
    canonicalMap.clear();
}

@Override
public boolean containsKey(Object key) {
    return map.containsKey(key);
}

@Override
public boolean containsValue(Object value) {
    return map.containsValue(value);
}

@Override
public Set<java.util.Map.Entry<K, V>> entrySet() {
    return map.entrySet();
}

@Override
public V get(Object key) {
    return map.get(key);
}

@Override
public boolean isEmpty() {
    return map.isEmpty();
}

@Override
public Set<K> keySet() {
    return map.keySet();
}

@Override
public V put(K key, V value) {
    V canonValue = canonicalMap.get(value);
    V previous = null;
    if (canonValue != null) {
        previous = map.put(key, canonValue);
    } else {
        previous = map.put(key, value);
        canonicalMap.put(value, value);
    }
    return previous;
}

@Override
public void putAll(Map<? extends K, ? extends V> m) {
    if (m == null) throw new NullPointerException("Can't exceute putAll for null-map argument");
    for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {
        map.put(entry.getKey(), entry.getValue());
    }
}

@Override
public V remove(Object key) {
    V removalValue = map.get(key);
    map.remove(key);
    if (!map.containsValue(removalValue)) {
        canonicalMap.remove(removalValue);
    }
    return removalValue;
}

@Override
public int size() {
    return map.size();
}

@Override
public Collection<V> values() {
    return map.values();
}

}

测试紧凑图:

    public static void main(String[] args) {
       Map<Integer, Integer> compactMap = new CompactMap<Integer, Integer>();
       //Map<Integer, Integer> usualMap = new HashMap<Integer, Integer>();
       for (int i=0; i<=1000000; i++) {
           compactMap.put(i, new Integer(i % 1000));
           // usualMap.put(i, new Integer(i % 1000));
       }
    }

用于CompactMap的VisualVM堆转储: 在此处输入图片说明

测试HashMap:

    public static void main(String[] args) {
       // Map<Integer, Integer> compactMap = new CompactMap<Integer, Integer>();
       Map<Integer, Integer> usualMap = new HashMap<Integer, Integer>();
       for (int i=0; i<=1000000; i++) {
           // compactMap.put(i, new Integer(i % 1000));
           usualMap.put(i, new Integer(i % 1000));
       }
    }

HashMap的VisualVM堆转储: 在此处输入图片说明

标准实现无法解决此问题,但是您可以轻松地自己滚动。 诀窍是保留一个HashMap<K,V> map来保存您的真实映射,另一个HashMap<V,V> canonical以确保您的值最终不会包含两个相等但不同的引用。

每当您想使用map.put(key,value)map添加内容时,都应该

  1. canonical图中查找value ,以查看您之前是否遇到过与此相同的事情。 如果canonical.get(value)返回canonical.get(value) ,则这是您应该放入map的规范表示形式。
  2. 如果在canonical中不存在它,则调用map.put(key,value)将其添加到主地图中,还将canonical.put(value,value)添加到主地图中,以便现在可以对与此相等的事物进行规范表示值。

也许您对Java中相等的含义感到困惑。 当您调用new时,将始终创建一个新实例。 确定它是否与另一个相等并不容易,因为它可能具有多个复杂类型的字段。

如果您改为执行以下操作:

Map<Integer, Integer> map = new HashMap<Integer, Integer>(); 
Integer answer = new Integer(42);
for (int i = 0; i <= 1000000; i++) { 
    map.put(i, answer); 
}

然后仅创建一个实例。 它将仅指向内存中的一个位置,如果更改,它将更改所有人(尽管Integer和所有其他包装器在Java中都是不可变的,这意味着不能更改)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM