blob: 12fc1394f0914319afb4d2920f598d9319c2bd73 [file] [log] [blame]
Michael Hanlca740d72015-06-16 10:04:58 +02001package de.ids_mannheim.korap.utils;
2
3import com.google.common.collect.Lists;
4import com.google.common.collect.MapMaker;
5
6import java.util.List;
7import java.util.concurrent.ConcurrentMap;
8
9import com.google.common.collect.Sets;
10
11import java.util.Collection;
12import java.util.Set;
13
Michael Hanlca740d72015-06-16 10:04:58 +020014/**
15 * A general purpose Multimap implementation for delayed processing and concurrent insertion/deletes.
16 * This code is based on an implementation by Guido Medina!
17 *
18 * @param <K> A comparable Key
19 * @param <V> A comparable Value
20 */
21
22/**
23 * User: hanl
24 * Date: 8/27/13
25 * Time: 11:18 AM
26 */
27
28public class ConcurrentMultiMap<K extends Comparable, V extends Comparable> {
29
30 private final int initialCapacity;
31 private final LockMap<K> locks;
32 private final ConcurrentMap<K, List<V>> cache;
33
Michael Hanl8abaf9e2016-05-23 16:46:35 +020034 public ConcurrentMultiMap () {
Michael Hanlca740d72015-06-16 10:04:58 +020035 this(16, 64);
36 }
37
Michael Hanl8abaf9e2016-05-23 16:46:35 +020038 public ConcurrentMultiMap (final int concurrencyLevel) {
Michael Hanlca740d72015-06-16 10:04:58 +020039 this(concurrencyLevel, 64);
40 }
41
Michael Hanl8abaf9e2016-05-23 16:46:35 +020042 public ConcurrentMultiMap (final int concurrencyLevel,
43 final int initialCapacity) {
Michael Hanlca740d72015-06-16 10:04:58 +020044 this.initialCapacity = initialCapacity;
Michael Hanl8abaf9e2016-05-23 16:46:35 +020045 cache = new MapMaker().concurrencyLevel(concurrencyLevel)
46 .initialCapacity(initialCapacity).makeMap();
Michael Hanlca740d72015-06-16 10:04:58 +020047 locks = new LockMap<K>(concurrencyLevel, initialCapacity);
48 }
49
Michael Hanl8abaf9e2016-05-23 16:46:35 +020050 public void put (final K key, final V value) {
Michael Hanlca740d72015-06-16 10:04:58 +020051 synchronized (locks.getLock(key)) {
52 List<V> set = cache.get(key);
53 if (set == null) {
54 set = Lists.newArrayListWithExpectedSize(initialCapacity);
55 cache.put(key, set);
56 }
57 set.add(value);
58 }
59 }
60
Michael Hanl8abaf9e2016-05-23 16:46:35 +020061 public void putAll (final K key, final Collection<V> values) {
Michael Hanlca740d72015-06-16 10:04:58 +020062 synchronized (locks.getLock(key)) {
63 List<V> set = cache.get(key);
64 if (set == null) {
65 set = Lists.newArrayListWithExpectedSize(initialCapacity);
66 cache.put(key, set);
67 }
68 set.addAll(values);
69 }
70 }
71
Michael Hanl8abaf9e2016-05-23 16:46:35 +020072 public List<V> remove (final K key) {
Michael Hanlca740d72015-06-16 10:04:58 +020073 synchronized (locks.getLock(key)) {
74 return cache.remove(key);
75 }
76 }
77
Michael Hanl8abaf9e2016-05-23 16:46:35 +020078 public void remove (final K key, final V value) {
Michael Hanlca740d72015-06-16 10:04:58 +020079 List<V> values = cache.get(key);
80 synchronized (locks.getLock(key)) {
81 values.remove(value);
82 }
83 }
84
Michael Hanl8abaf9e2016-05-23 16:46:35 +020085 public Set<K> getKeySet () {
Michael Hanlca740d72015-06-16 10:04:58 +020086 return cache.keySet();
87 }
88
Michael Hanl8abaf9e2016-05-23 16:46:35 +020089 public int size () {
Michael Hanlca740d72015-06-16 10:04:58 +020090 return cache.size();
91 }
92
Michael Hanl8abaf9e2016-05-23 16:46:35 +020093 public boolean containsKey (K key) {
Michael Hanlca740d72015-06-16 10:04:58 +020094 return cache.containsKey(key);
95 }
96
Michael Hanl8abaf9e2016-05-23 16:46:35 +020097 public List<V> get (K key) {
Michael Hanlca740d72015-06-16 10:04:58 +020098 return cache.get(key);
99 }
100
Michael Hanlca740d72015-06-16 10:04:58 +0200101 public class LockMap<K extends Comparable> {
102 private final ConcurrentMap<K, Object> locks;
103
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200104 public LockMap () {
Michael Hanlca740d72015-06-16 10:04:58 +0200105 this(16, 64);
106 }
107
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200108 public LockMap (final int concurrencyLevel) {
Michael Hanlca740d72015-06-16 10:04:58 +0200109 this(concurrencyLevel, 64);
110 }
111
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200112 public LockMap (final int concurrencyLevel, final int initialCapacity) {
113 locks = new MapMaker().concurrencyLevel(concurrencyLevel)
114 .initialCapacity(initialCapacity).weakValues().makeMap();
Michael Hanlca740d72015-06-16 10:04:58 +0200115 }
116
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200117 public Object getLock (final K key) {
Michael Hanlca740d72015-06-16 10:04:58 +0200118 final Object object = new Object();
119 Object lock = locks.putIfAbsent(key, object);
120 return lock == null ? object : lock;
121 }
122
123 }
124
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200125 public String toString () {
Michael Hanlca740d72015-06-16 10:04:58 +0200126 return cache.toString();
127 }
128
Michael Hanlca740d72015-06-16 10:04:58 +0200129}