blob: 8dba954ed288a6cd26730dcda385498932831e17 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.pig.impl.util;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* An implementation of multi-map. We can't use Apache commons
* MultiValueMap because it isn't serializable. And we don't want to use
* MultiHashMap, as it is marked deprecated.
*
* This class can't extend Map, because it needs to change the semantics of
* put, so that you give it one key and one value, and it either creates a
* new entry with the key and a new collection of value (if the is not yet
* in the map) or adds the values to the existing collection for the key
* (if the key is already in the map).
*/
public class MultiMap<K, V> implements Serializable {
// Change this if you modify the class.
static final long serialVersionUID = 2L;
protected Map<K, ArrayList<V>> mMap = null;
public MultiMap() {
mMap = new HashMap<K, ArrayList<V>>();
}
/**
* @param size Initial size of the map
*/
public MultiMap(int size) {
mMap = new HashMap<K, ArrayList<V>>(size);
}
/**
* Add an element to the map.
* @param key The key to store the value under. If the key already
* exists the value will be added to the collection for that key, it
* will not replace the existing value (as in a standard map).
* @param value value to store.
*/
public void put(K key, V value) {
ArrayList<V> list = mMap.get(key);
if (list == null) {
list = new ArrayList<V>();
list.add(value);
mMap.put(key, list);
} else {
list.add(value);
}
}
/**
* Add a key to the map with a collection of elements.
* @param key The key to store the value under. If the key already
* exists the value will be added to the collection for that key, it
* will not replace the existing value (as in a standard map).
* @param values collection of values to store.
*/
public void put(K key, Collection<V> values) {
ArrayList<V> list = mMap.get(key);
if (list == null) {
list = new ArrayList<V>(values);
mMap.put(key, list);
} else {
list.addAll(values);
}
}
/**
* Get the collection of values associated with a given key.
* @param key Key to fetch values for.
* @return list of values, or null if the key is not in the map.
*/
public List<V> get(K key) {
return mMap.get(key);
}
/**
* Remove one value from an existing key. If that is the last value
* for the key, then remove the key too.
* @param key Key to remove the value from.
* @param value Value to remove.
* @return The value being removed, or null if the key or value does
* not exist.
*/
public V remove(K key, V value) {
ArrayList<V> list = mMap.get(key);
if (list == null) return null;
Iterator<V> i = list.iterator();
V keeper = null;
while (i.hasNext()) {
keeper = i.next();
if (keeper.equals(value)) {
i.remove();
break;
}
}
if (list.size() == 0) {
mMap.remove(key);
}
return keeper;
}
/**
* Remove all the values associated with the given key
* @param key the key to be removed
* @return list of all value being removed
*/
public Collection<V> removeKey(K key) {
return mMap.remove(key) ;
}
/**
* Get a set of all the keys in this map.
* @return Set of keys.
*/
public Set<K> keySet() {
return mMap.keySet();
}
/**
* Get a single collection of all the values in the map. All of the
* values in the map will be conglomerated into one collection. There
* will not be any duplicate removal.
* @return collection of values.
*/
public Collection<V> values() {
Set<K> keys = mMap.keySet();
int size = 0;
for (K k : keys) {
size += mMap.get(k).size();
}
Collection<V> values = new ArrayList<V>(size);
for (K k : keys) {
values.addAll(mMap.get(k));
}
return values;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
Set<K> keys = mMap.keySet();
boolean hasNext = false;
sb.append("{");
for (K k : keys) {
if(hasNext) {
sb.append(",");
} else {
hasNext = true;
}
sb.append(k.toString() + "=");
sb.append(mMap.get(k));
}
sb.append("}");
return sb.toString();
}
/**
* Get the number of keys in the map.
* @return number of keys.
*/
public int size() {
return mMap.size();
}
public boolean isEmpty() {
return mMap.isEmpty();
}
public void clear() {
mMap.clear();
}
public boolean containsKey(K key) {
return mMap.containsKey(key);
}
public boolean containsValue(V val) {
return mMap.containsValue(val);
}
}