| Michael Hanl | ca740d7 | 2015-06-16 10:04:58 +0200 | [diff] [blame] | 1 | package de.ids_mannheim.korap.utils; |
| 2 | |
| 3 | import com.google.common.collect.Lists; |
| 4 | import com.google.common.collect.MapMaker; |
| 5 | |
| 6 | import java.util.List; |
| 7 | import java.util.concurrent.ConcurrentMap; |
| 8 | |
| 9 | import com.google.common.collect.Sets; |
| 10 | |
| 11 | import java.util.Collection; |
| 12 | import java.util.Set; |
| 13 | |
| Michael Hanl | ca740d7 | 2015-06-16 10:04:58 +0200 | [diff] [blame] | 14 | /** |
| 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 | |
| 28 | public 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 Hanl | 8abaf9e | 2016-05-23 16:46:35 +0200 | [diff] [blame] | 34 | public ConcurrentMultiMap () { |
| Michael Hanl | ca740d7 | 2015-06-16 10:04:58 +0200 | [diff] [blame] | 35 | this(16, 64); |
| 36 | } |
| 37 | |
| Michael Hanl | 8abaf9e | 2016-05-23 16:46:35 +0200 | [diff] [blame] | 38 | public ConcurrentMultiMap (final int concurrencyLevel) { |
| Michael Hanl | ca740d7 | 2015-06-16 10:04:58 +0200 | [diff] [blame] | 39 | this(concurrencyLevel, 64); |
| 40 | } |
| 41 | |
| Michael Hanl | 8abaf9e | 2016-05-23 16:46:35 +0200 | [diff] [blame] | 42 | public ConcurrentMultiMap (final int concurrencyLevel, |
| 43 | final int initialCapacity) { |
| Michael Hanl | ca740d7 | 2015-06-16 10:04:58 +0200 | [diff] [blame] | 44 | this.initialCapacity = initialCapacity; |
| Michael Hanl | 8abaf9e | 2016-05-23 16:46:35 +0200 | [diff] [blame] | 45 | cache = new MapMaker().concurrencyLevel(concurrencyLevel) |
| 46 | .initialCapacity(initialCapacity).makeMap(); |
| Michael Hanl | ca740d7 | 2015-06-16 10:04:58 +0200 | [diff] [blame] | 47 | locks = new LockMap<K>(concurrencyLevel, initialCapacity); |
| 48 | } |
| 49 | |
| Michael Hanl | 8abaf9e | 2016-05-23 16:46:35 +0200 | [diff] [blame] | 50 | public void put (final K key, final V value) { |
| Michael Hanl | ca740d7 | 2015-06-16 10:04:58 +0200 | [diff] [blame] | 51 | 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 Hanl | 8abaf9e | 2016-05-23 16:46:35 +0200 | [diff] [blame] | 61 | public void putAll (final K key, final Collection<V> values) { |
| Michael Hanl | ca740d7 | 2015-06-16 10:04:58 +0200 | [diff] [blame] | 62 | 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 Hanl | 8abaf9e | 2016-05-23 16:46:35 +0200 | [diff] [blame] | 72 | public List<V> remove (final K key) { |
| Michael Hanl | ca740d7 | 2015-06-16 10:04:58 +0200 | [diff] [blame] | 73 | synchronized (locks.getLock(key)) { |
| 74 | return cache.remove(key); |
| 75 | } |
| 76 | } |
| 77 | |
| Michael Hanl | 8abaf9e | 2016-05-23 16:46:35 +0200 | [diff] [blame] | 78 | public void remove (final K key, final V value) { |
| Michael Hanl | ca740d7 | 2015-06-16 10:04:58 +0200 | [diff] [blame] | 79 | List<V> values = cache.get(key); |
| 80 | synchronized (locks.getLock(key)) { |
| 81 | values.remove(value); |
| 82 | } |
| 83 | } |
| 84 | |
| Michael Hanl | 8abaf9e | 2016-05-23 16:46:35 +0200 | [diff] [blame] | 85 | public Set<K> getKeySet () { |
| Michael Hanl | ca740d7 | 2015-06-16 10:04:58 +0200 | [diff] [blame] | 86 | return cache.keySet(); |
| 87 | } |
| 88 | |
| Michael Hanl | 8abaf9e | 2016-05-23 16:46:35 +0200 | [diff] [blame] | 89 | public int size () { |
| Michael Hanl | ca740d7 | 2015-06-16 10:04:58 +0200 | [diff] [blame] | 90 | return cache.size(); |
| 91 | } |
| 92 | |
| Michael Hanl | 8abaf9e | 2016-05-23 16:46:35 +0200 | [diff] [blame] | 93 | public boolean containsKey (K key) { |
| Michael Hanl | ca740d7 | 2015-06-16 10:04:58 +0200 | [diff] [blame] | 94 | return cache.containsKey(key); |
| 95 | } |
| 96 | |
| Michael Hanl | 8abaf9e | 2016-05-23 16:46:35 +0200 | [diff] [blame] | 97 | public List<V> get (K key) { |
| Michael Hanl | ca740d7 | 2015-06-16 10:04:58 +0200 | [diff] [blame] | 98 | return cache.get(key); |
| 99 | } |
| 100 | |
| Michael Hanl | ca740d7 | 2015-06-16 10:04:58 +0200 | [diff] [blame] | 101 | public class LockMap<K extends Comparable> { |
| 102 | private final ConcurrentMap<K, Object> locks; |
| 103 | |
| Michael Hanl | 8abaf9e | 2016-05-23 16:46:35 +0200 | [diff] [blame] | 104 | public LockMap () { |
| Michael Hanl | ca740d7 | 2015-06-16 10:04:58 +0200 | [diff] [blame] | 105 | this(16, 64); |
| 106 | } |
| 107 | |
| Michael Hanl | 8abaf9e | 2016-05-23 16:46:35 +0200 | [diff] [blame] | 108 | public LockMap (final int concurrencyLevel) { |
| Michael Hanl | ca740d7 | 2015-06-16 10:04:58 +0200 | [diff] [blame] | 109 | this(concurrencyLevel, 64); |
| 110 | } |
| 111 | |
| Michael Hanl | 8abaf9e | 2016-05-23 16:46:35 +0200 | [diff] [blame] | 112 | public LockMap (final int concurrencyLevel, final int initialCapacity) { |
| 113 | locks = new MapMaker().concurrencyLevel(concurrencyLevel) |
| 114 | .initialCapacity(initialCapacity).weakValues().makeMap(); |
| Michael Hanl | ca740d7 | 2015-06-16 10:04:58 +0200 | [diff] [blame] | 115 | } |
| 116 | |
| Michael Hanl | 8abaf9e | 2016-05-23 16:46:35 +0200 | [diff] [blame] | 117 | public Object getLock (final K key) { |
| Michael Hanl | ca740d7 | 2015-06-16 10:04:58 +0200 | [diff] [blame] | 118 | final Object object = new Object(); |
| 119 | Object lock = locks.putIfAbsent(key, object); |
| 120 | return lock == null ? object : lock; |
| 121 | } |
| 122 | |
| 123 | } |
| 124 | |
| Michael Hanl | 8abaf9e | 2016-05-23 16:46:35 +0200 | [diff] [blame] | 125 | public String toString () { |
| Michael Hanl | ca740d7 | 2015-06-16 10:04:58 +0200 | [diff] [blame] | 126 | return cache.toString(); |
| 127 | } |
| 128 | |
| Michael Hanl | ca740d7 | 2015-06-16 10:04:58 +0200 | [diff] [blame] | 129 | } |