简体   繁体   English

紧凑的java.util.Map实现

[英]compact java.util.Map implementation

Is there any java.util.Map implementation that do not stores Values of equals ValueObjects but store only one of them? 是否有任何java.util.Map实现不存储等于ValueObjects的值而仅存储其中之一? (references of this Objects are different, but they are equals) (此对象的引用不同,但是相等)

here is a little test code : 这是一些测试代码:

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

and visual VM shows : 2 million of instances of Integer these objects are equals but Map stores all of them 和可视VM显示:这些对象的200万个Integer实例相等,但Map存储了所有实例

在此处输入图片说明

Just for history logging I coded chiastic-security's solution and test it to compare with HashMap: 仅出于历史记录的目的,我编码了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();
}

}

test compact map: 测试紧凑图:

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

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

test HashMap : 测试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));
       }
    }

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

The standard implementations don't handle this, but you could roll your own easily enough. 标准实现无法解决此问题,但是您可以轻松地自己滚动。 The trick is to keep hold of a HashMap<K,V> map to hold your real mappings, and another HashMap<V,V> canonical to make sure your values don't end up containing two equal but non-identical references. 诀窍是保留一个HashMap<K,V> map来保存您的真实映射,另一个HashMap<V,V> canonical以确保您的值最终不会包含两个相等但不同的引用。

Whenever you want to add something to your map with map.put(key,value) , you should 每当您想使用map.put(key,value)map添加内容时,都应该

  1. Look up value in your canonical map to see whether you've encountered something equal to this before. canonical图中查找value ,以查看您之前是否遇到过与此相同的事情。 If canonical.get(value) returns something, then this is the canonical representation you should put into map . 如果canonical.get(value)返回canonical.get(value) ,则这是您应该放入map的规范表示形式。
  2. If it doesn't exist in canonical , then call map.put(key,value) to add it to the main map, and also canonical.put(value,value) so that you now have a canonical representation for things equal to this value. 如果在canonical中不存在它,则调用map.put(key,value)将其添加到主地图中,还将canonical.put(value,value)添加到主地图中,以便现在可以对与此相等的事物进行规范表示值。

Maybe you are confused to the meanings of equality in Java. 也许您对Java中相等的含义感到困惑。 When you call new, a new instance will always be created. 当您调用new时,将始终创建一个新实例。 It is not trivial to determine if it is "equal" to another one, as it might have several fields of several complex types. 确定它是否与另一个相等并不容易,因为它可能具有多个复杂类型的字段。

If you do something like this instead: 如果您改为执行以下操作:

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

Then only one instance is created. 然后仅创建一个实例。 The would only point to one place in memory, that, if altered, would change everyone (though Integer and every other wrapper are immutable in Java, meaning the cannot change). 它将仅指向内存中的一个位置,如果更改,它将更改所有人(尽管Integer和所有其他包装器在Java中都是不可变的,这意味着不能更改)。

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

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