/* * 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); } }