是否有任何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));
}
和visual 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堆转储:
答案 0 :(得分:4)
标准实施不能解决这个问题,但你可以轻松地自己动手。诀窍是保持HashMap<K,V> map
以保持您的真实映射,另一个HashMap<V,V> canonical
以确保您的值不会包含两个相同但不相同的引用。
每当您想使用map
向map.put(key,value)
添加内容时,您应该
value
地图中查找canonical
,看看您之前是否遇到过与此相同的内容。如果canonical.get(value)
返回某些内容,那么这是您应该放入map
的规范表示。canonical
中不存在,则调用map.put(key,value)
将其添加到主地图,并canonical.put(value,value)
,以便您现在拥有规范的事物代表等于这个值。答案 1 :(得分:0)
也许你对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中都是不可变的,这意味着它不会改变)。