io.neba.core.util.ConcurrentDistinctMultiValueMap Maven / Gradle / Ivy
/*
Copyright 2013 the original author or authors.
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 io.neba.core.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* A thread-safe storage for key -> value associations. Unlike
* {@link ConcurrentHashMap}, this map synchronizes writes since the multi-value
* associations require two independent read/writes to the map state; one to
* retrieve an existing collection, a second one to add another value to it.
* Note that it is not save to modify the contents of the collection
* returned by this map since it is still based on the contents of this map and
* not a shallow copy for performance considerations.
*
* In addition, the values stored in this map are distinct, i.e.
* if a value already exists in the values collection it is {@link Collection#remove(Object) removed}
* prior to the insertion of the new element.
*
* @param The key's type.
* @param The value's type.
* @author Olaf Otto
*/
public class ConcurrentDistinctMultiValueMap {
private final Map> store = new ConcurrentHashMap<>(128);
public Collection get(K key) {
return this.store.get(key);
}
public synchronized void put(K key, V value) {
Collection vs = getOrCreate(key);
vs.add(value);
}
public void clear() {
this.store.clear();
}
public Collection remove(K key) {
return this.store.remove(key);
}
public Collection> values() {
return this.store.values();
}
public synchronized void put(K key, Collection values) {
Collection vs = getOrCreate(key);
vs.addAll(values);
}
private Collection getOrCreate(K key) {
Collection vs = this.store.get(key);
if (vs == null) {
vs = new ConcurrentLinkedDistinctQueue<>();
this.store.put(key, vs);
}
return vs;
}
/**
* @return a shallow copy of the current state of this map. Note that the
* map values (the collection) are also copied; it is thus save to
* modify the state of the returned map and the state of the
* collections returned as the map values. Never returns null.
*/
public Map> getContents() {
HashMap> contents = new HashMap<>(this.store.size());
Set>> entries = this.store.entrySet();
for (Entry> entry : entries) {
contents.put(entry.getKey(), new ArrayList<>(entry.getValue()));
}
return contents;
}
public int size() {
return this.store.size();
}
public boolean isEmpty() {
return this.store.isEmpty();
}
/**
* Removes the given value from all collections stored under any key.
*
* @param value must not be null
.
*/
public void removeValue(V value) {
if (value == null) {
throw new IllegalArgumentException("Method argument value must not be null.");
}
for (Collection values : values()) {
values.remove(value);
}
}
}