org.jvnet.hk2.component.MultiMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache Show documentation
Show all versions of ehcache Show documentation
Ehcache is an open source, standards-based cache used to boost performance,
offload the database and simplify scalability. Ehcache is robust, proven and full-featured and
this has made it the most widely-used Java-based cache.
The newest version!
/*
* Copyright (c) 2007, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package org.jvnet.hk2.component;
import java.io.Serializable;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Map from a key to multiple values.
* Order is significant among values, and null values are allowed, although null keys are not.
*
* @author Kohsuke Kawaguchi
* @author Jerome Dochez
* @param The key of the multi-map
* @param The types in the multi-map
*/
public class MultiMap implements Serializable, Cloneable {
/**
* For serialization
*/
private static final long serialVersionUID = 893592003056170756L;
private final Map> store = new LinkedHashMap>();
/**
* Creates an empty multi-map with default concurrency controls
*/
public MultiMap() {
}
/**
* Copy constructor.
*
* @param base map to copy
*/
public MultiMap(MultiMap base) {
this();
for (Map.Entry> e : base.entrySet()) {
List value = newList(e.getValue());
if (!value.isEmpty()) {
store.put(e.getKey(), newList(e.getValue()));
}
}
}
/**
* Creates an optionally populated list to be used as an entry in the map.
*
* @param initialVal
* @return
*/
private List newList(Collection initialVals) {
if (null == initialVals) {
return new LinkedList();
}
return new LinkedList(initialVals);
}
/**
* Returns the set of keys associated with this MultiMap
*
* @return The set of keys currently available in this MultiMap. Will not return null,
* but may return a Set of lenght zero
*/
public Set keySet() {
return store.keySet();
}
/**
* Adds one more key-value pair.
*
* @param k key to store the entry under
* @param v value to store in the k's values.
*/
public final void add(K k, V v) {
List l = store.get(k);
if (l == null) {
l = newList(null);
store.put(k, l);
}
l.add(v);
}
/**
* Replaces all the existing values associated with the key
* by the given value. If v is empty the key k will
* be removed from the MultiMap.
*
* @param k key for the values
* @param v Can be null or empty.
*/
public void set(K k, Collection v) {
List addMe = newList(v);
if (addMe.isEmpty()) {
store.remove(k);
}
else {
store.put(k, newList(v));
}
}
/**
* Replaces all the existing values associated with the key
* by the given single value.
*
* @param k key for the values
* @param v singleton value for k key
*
* This is short for set(k,Collections.singleton(v))
*/
public void set(K k, V v) {
List vlist = newList(null);
vlist.add(v);
store.put(k, vlist);
}
/**
* Returns the elements indexed by the provided key
*
* @param k key for the values
* @return Can be empty but never null. Read-only.
*/
public final List get(K k) {
List l = store.get(k);
if (l == null) {
return Collections.emptyList();
}
return Collections.unmodifiableList(l);
}
/**
* This method merges all of the keys and values from another
* MultiMap into this MultiMap. If a key/value pair is
* found in both MultiMaps it is not re-added to this
* MultiMap, but is instead discarded
*
* @param another The MultiMap from which to add values
* to this MultiMap. If null this method does nothing
*/
public void mergeAll(MultiMap another) {
if (another == null) return;
for (Map.Entry> entry : another.entrySet()) {
List ourList = store.get(entry.getKey());
if (null == ourList) {
ourList = newList(entry.getValue());
if (!ourList.isEmpty()) {
store.put(entry.getKey(), ourList);
}
}
else {
for (V v : entry.getValue()) {
if (!ourList.contains(v)) {
ourList.add(v);
}
}
}
}
}
/**
* Package private (for getting the raw map for direct manipulation by the habitat)
*
* @param k the key
* @return
*/
private final List _get(K k) {
List l = store.get(k);
if (l == null) {
return Collections.emptyList();
}
return l;
}
/**
* Checks if the map contains the given key.
*
* @param k key to test
* @return true if the map contains at least one element for this key
*/
public boolean containsKey(K k) {
return !get(k).isEmpty();
}
/**
* Checks if the map contains the given key(s), also extending the search
* to including the sub collection.
*
* @param k1 key from top collection
* @param k2 key (value) from inner collection
* @return true if the map contains at least one element for these keys
*/
public boolean contains(K k1, V k2) {
List list = _get(k1);
return list.contains(k2);
}
/**
* Removes an key value from the map
*
* @param key key to be removed
* @return the value stored under this key or null if there was none
*/
public List remove(K key) {
return store.remove(key);
}
/**
* Removes an key value pair from the map. If the list of
* entries for that key is empty after the remove
* it will be removed from the set of keys
*
* @param key key to be removed
* @param entry the entry to be removed from the key'ed list
* @return true if there was none that was deleted
*/
public boolean remove(K key, V entry) {
List list = store.get(key);
if (list == null) return false;
boolean retVal = list.remove(entry);
if (list.isEmpty()) {
store.remove(key);
}
return retVal;
}
/**
* Gets the first value if any, or null.
*
* This is useful when you know the given key only has one value and you'd like
* to get to that value.
*
* @param k key for the values
* @return null if the key has no values or it has a value but the value is null.
*/
public V getOne(K k) {
return getFirst(k);
}
private V getFirst(K k) {
List lst = store.get(k);
if (null == lst) {
return null;
}
if (lst.isEmpty()) {
return null;
}
return lst.get(0);
}
/**
* Lists up all entries.
*
* @return a {@link java.util.Set} of {@link java.util.Map.Entry} of entries
*/
public Set>> entrySet() {
return store.entrySet();
}
/**
* @return the map as "key=value1,key=value2,...."
*/
public String toCommaSeparatedString() {
StringBuilder buf = new StringBuilder();
for (Map.Entry> e : entrySet()) {
for (V v : e.getValue()) {
if (buf.length() > 0) {
buf.append(',');
}
buf.append(e.getKey()).append('=').append(v);
}
}
return buf.toString();
}
/**
* Creates a copy of the map that contains the exact same key and value set.
* Keys and values won't cloned.
*/
@Override
public MultiMap clone() throws CloneNotSupportedException {
super.clone();
return new MultiMap(this);
}
/**
* Returns the size of the map. This returns the numbers
* of keys in the map, not the number of values
*
* @return integer or 0 if the map is empty
*/
public int size() {
return store.size();
}
@Override
public int hashCode() {
return store.hashCode();
}
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object another) {
if (another == null ||
!(another instanceof MultiMap)) return false;
MultiMap other = (MultiMap) another;
return store.equals(other.store);
}
private final static String NEWLINE = AccessController.doPrivileged(new PrivilegedAction() {
@Override
public String run() {
return System.getProperty("line.separator");
}
});
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("{");
for (final K key : store.keySet()) {
builder.append(key).append(": ");
builder.append(store.get(key));
builder.append(NEWLINE);
}
builder.append("}");
return builder.toString();
}
}