/**
Copyright 2015 Tim Engler, Rareventure LLC
This file is part of Tiny Travel Tracker.
Tiny Travel Tracker is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Tiny Travel Tracker is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Tiny Travel Tracker. If not, see <http://www.gnu.org/licenses/>.
*/
package com.rareventure.util;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
/**
* A HashMap that saves multiple values for several puts with the same key
*
* @author Tim Engler
*/
public class MultiValueHashMap<K,V> {
public static final LinkedList EMPTY_LIST = new LinkedList();
private HashMap<K,List<V>> map = new HashMap<K,List<V>>();
protected int valuesSize = 0;
public MultiValueHashMap() {
}
public void clear() {
map.clear();
valuesSize = 0;
}
/**
* Returns true if the hashmap contains the given value
*/
public boolean contains(Object value) {
for (Iterator i = map.entrySet().iterator(); i.hasNext();) {
List v = (List) i.next();
for (Iterator i2 = v.iterator(); i2.hasNext();) {
if (i2.next().equals(value))
return true;
}
}
return false;
}
/**
* Returns list of values for a key
*/
public List<V> get(K key) {
List<V> values = map.get(key);
if (values == null || values.size() == 0) // Could happen if value was
// removed
return null;
return values;
}
/**
* Returns first value for a key, or null if there aren't any
*/
public V getFirst(K key) {
List<V> values = get(key);
if(values == null)
return null;
return values.get(0);
}
/**
* Returns a list of all values for a key. If key doesn't exist, returns an
* empty list.
*/
public List<V> gets(K key) {
List<V> values = (List<V>) map.get(key);
if (values == null) {
return EMPTY_LIST;
}
return values;
}
public Set<K> keySet() {
return map.keySet();
}
/**
* Puts a value for a key. If there is more then one value, each additional
* value will be added as another value for the key.
*/
public void put(K key, V value) {
List<V> values = (List<V>) map.get(key);
if (values == null) {
values = new ArrayList<V>();
map.put(key, values);
}
values.add(value);
valuesSize++;
}
/**
* Removes the first value for a key. If there is only one value for a key,
* the key, value pair is removed.
*/
public V remove(K key, V value) {
List<V> values = (List<V>) map.get(key);
if (values == null)
return null;
if (values.remove(value)) {
valuesSize--;
if(values.size() == 0)
map.remove(key); //remove the key, so it won't be present in the key set
return value;
}
return null;
}
public int size() {
return map.size();
}
/**
* @return all the values for the multi value hash map
*/
public Collection<V> values() {
return new AbstractCollection<V>()
{
@Override
public Iterator<V> iterator() {
return new Iterator<V>()
{
private boolean gotNextValue;
private boolean atEnd;
private V next;
private Iterator<List<V>> mapValuesIterator = map.values().iterator();
private Iterator<V> subIterator = null;
private void _getNextValue() {
while((subIterator == null || !subIterator.hasNext()) && mapValuesIterator.hasNext())
subIterator = mapValuesIterator.next().iterator();
if(!subIterator.hasNext() && !mapValuesIterator.hasNext())
atEnd = true;
else
next = subIterator.next();
gotNextValue = true;
}
public boolean hasNext() {
if(gotNextValue)
return !atEnd;
_getNextValue();
return !atEnd;
}
public V next() {
if(gotNextValue)
{
gotNextValue = false;
return next;
}
_getNextValue();
gotNextValue = false;
return next;
}
public void remove() {
throw new IllegalStateException("You can't do that!");
}
};
}
@Override
public int size() {
return valuesSize ;
}
};
}
public Set<Map.Entry<K,List<V>>> entrySet() {
return map.entrySet();
}
/**
* Removes all values associated with key
*/
public void remove(K name) {
map.remove(name);
}
}