/* * Copyright (c) 2013-2015 Josef Hardi <josef.hardi@gmail.com> * * Licensed 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 com.obidea.semantika.util; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.slf4j.Logger; public class MultiMap<K, V> implements Serializable { private static final long serialVersionUID = 629451L; private final Map<K, Collection<V>> mMap; private int mSize = 0; private boolean mUseSets = true; private static Logger LOG = LogUtils.createLogger("semantika.utility"); //$NON-NLS-1$ public MultiMap() { this(false); } public MultiMap(boolean useSets) { mMap = new HashMap<K, Collection<V>>(); mUseSets = useSets; } public boolean put(K key, V value) { Collection<V> set = mMap.get(key); if (set == null) { set = createCollection(); mMap.put(key, set); } boolean toReturn = set.add(value); if (toReturn) { mSize = -1; // a flag that the map was updated } return toReturn; } private Collection<V> createCollection() { Collection<V> toReturn; if (mUseSets) { toReturn = new HashSet<V>(); } else { toReturn = new ArrayList<V>(); } return toReturn; } public void setEntry(K key, Collection<V> values) { mMap.put(key, values); this.mSize = -1; } /** * Returns a mutable set of values connected to the key; if no value is * connected, returns an immutable empty set * * @param key * The map key. * @return The collection of values connected with the key */ public Collection<V> get(K key) { final Collection<V> collection = mMap.get(key); if (collection != null) { return collection; } return Collections.emptyList(); } /** * @return The key set. */ public Set<K> keySet() { return mMap.keySet(); } /** * @return All values in the map as a set. */ public Set<V> getAllValues() { Set<V> toReturn = new HashSet<V>(); for (Collection<V> collection : mMap.values()) { for (V v : collection) { boolean succeed = toReturn.add(v); if (!succeed) { LOG.warn("The set already contained the value: {}", v.toString()); //$NON-NLS-1$ } } } return toReturn; } /** * Removes the collection of values connected to the key * * @param key * The map key. */ public boolean remove(K key) { if (mMap.remove(key) != null) { mSize = -1; return true; } return false; } /** * Removes the value connected to the key; if there is more than one value * connected to the key, only one is removed * * @param key * The map key. * @param value * The value to remove in the corresponded collection. */ public boolean remove(K key, V value) { Collection<V> c = mMap.get(key); if (c != null) { boolean toReturn = c.remove(value); // if false, no change was actually made - skip the rest if (!toReturn) { return false; } mSize = -1; if (c.isEmpty()) { mMap.remove(key); } return true; } return false; } /** * @return The size of the multimap (sum of all the sizes of the sets) */ public int size() { if (mSize < 0) { mSize = getAllValues().size(); } return mSize; } /** * @return Returns true if the pairing <key, value> is in the map. */ public boolean contains(K key, V value) { final Collection<V> collection = mMap.get(key); if (collection == null) { return false; } return collection.contains(value); } /** * @return Returns true if the map contains the key. */ public boolean containsKey(K key) { return mMap.containsKey(key); } /** * @return Returns true if the map contains the value. */ public boolean containsValue(V value) { for (Collection<V> c : mMap.values()) { if (c.contains(value)) { return true; } } return false; } public void clear() { mMap.clear(); mSize = 0; } @Override public String toString() { return "MultiMap " + size() + "\n" + mMap.toString(); //$NON-NLS-1$ //$NON-NLS-2$ } public void putAll(MultiMap<K, V> otherMap) { for (K k : otherMap.keySet()) { putAll(k, otherMap.get(k)); } } public void putAll(K key, Collection<V> values) { Collection<V> set = mMap.get(key); if (set == null) { set = createCollection(); setEntry(key, set); } set.addAll(values); mSize = -1; } public boolean isValueSetsEqual() { if (mMap.size() < 2) { return true; } List<Collection<V>> list = new ArrayList<Collection<V>>(mMap.values()); for (int i = 1; i < list.size(); i++) { if (!list.get(0).equals(list.get(i))) { return false; } } return true; } public boolean isEmpty() { return (size() == 0) ? true : false; } }