package se.emilsjolander.stickylistheaders; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * a hash map can maintain an one-to-many relationship which the value only belongs to one “one” part * and the map also support getKey by value quickly * * @author lsjwzh */ class DistinctMultiHashMap<TKey,TItemValue> { private IDMapper<TKey, TItemValue> mIDMapper; interface IDMapper<TKey,TItemValue>{ public Object keyToKeyId(TKey key); public TKey keyIdToKey(Object keyId); public Object valueToValueId(TItemValue value); public TItemValue valueIdToValue(Object valueId); } LinkedHashMap<Object,List<TItemValue>> mKeyToValuesMap = new LinkedHashMap<Object, List<TItemValue>>(); LinkedHashMap<Object,TKey> mValueToKeyIndexer = new LinkedHashMap<Object, TKey>(); DistinctMultiHashMap(){ this(new IDMapper<TKey, TItemValue>() { @Override public Object keyToKeyId(TKey key) { return key; } @Override public TKey keyIdToKey(Object keyId) { return (TKey) keyId; } @Override public Object valueToValueId(TItemValue value) { return value; } @Override public TItemValue valueIdToValue(Object valueId) { return (TItemValue) valueId; } }); } DistinctMultiHashMap(IDMapper<TKey, TItemValue> idMapper){ mIDMapper = idMapper; } public List<TItemValue> get(TKey key){ //todo immutable return mKeyToValuesMap.get(mIDMapper.keyToKeyId(key)); } public TKey getKey(TItemValue value){ return mValueToKeyIndexer.get(mIDMapper.valueToValueId(value)); } public void add(TKey key,TItemValue value){ Object keyId = mIDMapper.keyToKeyId(key); if(mKeyToValuesMap.get(keyId)==null){ mKeyToValuesMap.put(keyId,new ArrayList<TItemValue>()); } //remove old relationship TKey keyForValue = getKey(value); if(keyForValue !=null){ mKeyToValuesMap.get(mIDMapper.keyToKeyId(keyForValue)).remove(value); } mValueToKeyIndexer.put(mIDMapper.valueToValueId(value), key); if(!containsValue(mKeyToValuesMap.get(mIDMapper.keyToKeyId(key)),value)) { mKeyToValuesMap.get(mIDMapper.keyToKeyId(key)).add(value); } } public void removeKey(TKey key){ if(mKeyToValuesMap.get(mIDMapper.keyToKeyId(key))!=null){ for (TItemValue value : mKeyToValuesMap.get(mIDMapper.keyToKeyId(key))){ mValueToKeyIndexer.remove(mIDMapper.valueToValueId(value)); } mKeyToValuesMap.remove(mIDMapper.keyToKeyId(key)); } } public void removeValue(TItemValue value){ if(getKey(value)!=null){ List<TItemValue> itemValues = mKeyToValuesMap.get(mIDMapper.keyToKeyId(getKey(value))); if(itemValues!=null){ itemValues.remove(value); } } mValueToKeyIndexer.remove(mIDMapper.valueToValueId(value)); } public void clear(){ mValueToKeyIndexer.clear(); mKeyToValuesMap.clear(); } public void clearValues(){ for (Map.Entry<Object,List<TItemValue>> entry:entrySet()){ if(entry.getValue()!=null){ entry.getValue().clear(); } } mValueToKeyIndexer.clear(); } public Set<Map.Entry<Object,List<TItemValue>>> entrySet(){ return mKeyToValuesMap.entrySet(); } public Set<Map.Entry<Object,TKey>> reverseEntrySet(){ return mValueToKeyIndexer.entrySet(); } public int size(){ return mKeyToValuesMap.size(); } public int valuesSize(){ return mValueToKeyIndexer.size(); } protected boolean containsValue(List<TItemValue> list,TItemValue value){ for (TItemValue itemValue :list){ if(mIDMapper.valueToValueId(itemValue).equals(mIDMapper.valueToValueId(value))){ return true; } } return false; } /** * @param position * @return */ public TItemValue getValueByPosition(int position){ Object[] vauleIdArray = mValueToKeyIndexer.keySet().toArray(); if(position>vauleIdArray.length){ throw new IndexOutOfBoundsException(); } Object valueId = vauleIdArray[position]; return mIDMapper.valueIdToValue(valueId); } }