簡體   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