package org.itsnat.droid.impl.util; import org.itsnat.droid.ItsNatDroidException; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.WeakHashMap; /** * Created by jmarranz on 16/06/14. */ public class WeakMapWithValue<Key,Value> { protected WeakHashMap<Value,Key> mapByValue = new WeakHashMap<Value,Key>(); protected HashMap<Key,WeakReference<Value>> mapByKey = new HashMap<Key,WeakReference<Value>>(); // Esta colección nos sirve para obtener el objeto valor y hacer algún tipo de cleaning /** Creates a new instance of NodeCache */ public WeakMapWithValue() { } public void copyTo(WeakMapWithValue<Key,Value> target) { for(Map.Entry<Key,WeakReference<Value>> entry : mapByKey.entrySet()) { Key key = entry.getKey(); WeakReference<Value> weakValue = entry.getValue(); Value value = weakValue.get(); // Puede ser null, significa que se ha perdido el Value, en el siguiente cleanUnused se normalizará if (value == null) continue; target.put(key,value); } } public Value removeByKey(Key key) { cleanUnused(); WeakReference<Value> weakValue = mapByKey.remove(key); if (weakValue == null) return null; Value value = weakValue.get(); if (value == null) return null; mapByValue.remove(value); return value; } public Key removeByValue(Value value) { cleanUnused(); Key key = mapByValue.remove(value); mapByKey.remove(key); return key; } public Value getValueByKey(Key key) { cleanUnused(); WeakReference<Value> weakValue = mapByKey.get(key); if (weakValue == null) return null; return weakValue.get(); // Puede ser null, significa que se ha perdido el Value, en el siguiente cleanUnused se normalizará } public Key getKeyByValue(Value value) { cleanUnused(); return mapByValue.get(value); } public Value put(Key key,Value value) { if (value == null) throw new ItsNatDroidException("null value is not allowed"); // Usar removeByKey cleanUnused(); mapByValue.put(value,key); WeakReference<Value> oldWeakValue = mapByKey.put(key,new WeakReference<Value>(value)); if (oldWeakValue == null) return null; return oldWeakValue.get(); } public boolean isEmpty() { cleanUnused(); boolean res = mapByKey.isEmpty(); if (res != mapByValue.isEmpty()) throw MiscUtil.internalError(); return res; } public void cleanUnused() { // No es muy eficiente pero jamás llegaremos ni a 10 ids por cada Layout. // mapByValue dismuinirá en elementos cuando se pierda un View, mapByKey no se enterará si no lo eliminamos explícitamente if (!mapByValue.isEmpty() && mapByValue.size() < mapByKey.size()) // mapByValue y mapByKey { for (Iterator<Map.Entry<Key, WeakReference<Value>>> it = mapByKey.entrySet().iterator(); it.hasNext(); ) { Map.Entry<Key, WeakReference<Value>> entry = it.next(); WeakReference<Value> weakRef = entry.getValue(); Value value = weakRef.get(); if (!mapByValue.containsKey(value)) it.remove(); } } // Despues aun así no hay 100% garantía de que los tamaños sean iguales, una colección puede disminuir en cualquier momento // a capricho del GC //if (mapByKey.size() != mapByValue.size()) throw new ItsNatDroidException("Internal Error"); } public static void test(String[] args) //throws Exception { WeakMapWithValue<Integer,String> map = new WeakMapWithValue<Integer,String>(); for(int i = 0; i < 1000; i++) { map.put(new Integer(i),new String(Integer.toString(i))); } System.gc(); map.cleanUnused(); } }