View Javadoc

1   /**
2    * 
3    */
4   package org.sourceforge.jemm.util.managed;
5   
6   import java.util.Collection;
7   import java.util.Map;
8   import java.util.Set;
9   import java.util.concurrent.ConcurrentHashMap;
10  
11  import org.sourceforge.jemm.util.managed.bucket.BucketLockedBucketFactory;
12  import org.sourceforge.jemm.util.managed.bucket.ECMapBucket;
13  import org.sourceforge.jemm.util.managed.bucket.ECMapBucketFactory;
14  
15  /**
16   * EnhancedConcurrentHashMap is similar to ConcurrentHashMap, but adds some extra 
17   * features.  The main extra method is 'perform' that allows an arbitrary action to 
18   * be performed whilst the lock on the given key is held (even if there is no entry
19   * in the map beforehand.
20   * 
21   * <B>N.B</B> This map does not support null values, if put(key,null) is used, contains(key) will return false.
22   * 
23   * @param <K> The key type. 
24   * @param <V>  The value type
25   *
26   * @author Rory Graves
27   */
28  public class EnhancedConcurrentHashMap<K,V> implements EnhancedConcurrentMap<K, V> {
29      /** The default number of locking buckets. */
30      public static final int DEFAULT_NO_BUCKETS = 8;
31  
32      private final int noBuckets;
33      private final ECMapBucket<K,V>[] buckets;
34      
35      /**
36       * Creates an EnhancedConcurrentHashMap with a default number of buckets.
37       */
38      public EnhancedConcurrentHashMap() {
39          this(new BucketLockedBucketFactory<K, V>(),DEFAULT_NO_BUCKETS);
40      }
41  
42      /** 
43       * Creates an EnhancedConcurrentHashMap with the given number of internal buckets.
44       * @param noBuckets
45       */
46      public EnhancedConcurrentHashMap(int noBuckets) {
47          this(new BucketLockedBucketFactory<K, V>(),noBuckets);
48      }
49    
50      @SuppressWarnings("unchecked") 
51      protected EnhancedConcurrentHashMap(ECMapBucketFactory<K, V> bucketFactory,int noBuckets) {
52          this.noBuckets = noBuckets;
53          this.buckets = new ECMapBucket[noBuckets];
54          for(int i=0; i<noBuckets; i++)
55              buckets[i] = bucketFactory.createBucket();
56      }
57      
58      /**
59       * Perform the given action whilst holding the lock on the given key.
60       * The action is able to access, update or clear the value associated 
61       * with the given key as part of the action.
62       * @param key The key to lock during the action.
63       * @param action The action to perform whilst the lock is held.
64       */
65      @Override public void perform(K key,ECMAction<K,V> action) {
66          getBucket(key).perform(key, action);
67      }
68      
69      /**
70       * Method to separate hashCodes values into segment buckets.
71       * @param keyHashCode of the item being bucketed
72       * @param noBuckets The number of segments available.
73       * @return The assigned segment (0...(noSegments-1))
74       */
75      private ECMapBucket<K, V> getBucket(K key) {
76          if(key == null)
77              throw new IllegalArgumentException("Key may not be null");
78  
79          int hashCode = key.hashCode(); 
80  
81          // Math.abs behaves oddly with Integer.MIN_VALUE
82          if(hashCode == Integer.MIN_VALUE) 
83              hashCode = 0;
84  
85          int absHashCode = Math.abs(hashCode);
86          return buckets[absHashCode % noBuckets];
87      }
88  
89      /**
90       * Atomically associate a value with a key, if a value is not already assocated with the given key.
91       * @param key The key
92       * @param value The new value to associate.
93       * @return The old value associated with 'key'
94       * @see ConcurrentHashMap#putIfAbsent(Object,Object)
95       */
96      @Override public V putIfAbsent(K key, V value) {
97          return getBucket(key).putIfAbsent(key,value);
98      }
99  
100     @SuppressWarnings("unchecked") 
101     @Override public boolean remove(Object key, Object value) {
102         return getBucket((K) key).remove((K) key,(V) value);
103     }
104 
105     @Override public V replace(K key, V value) {
106         return getBucket(key).replace(key,value);
107     }
108 
109     @Override public boolean replace(K key, V oldValue, V newValue) {
110         return getBucket(key).replace(key,oldValue,newValue);
111     }
112 
113     /** 
114      * Clear all the entries in this map.
115      * <B>N.B.</B> This is not a thread safe operation, inserts that occur simultaneously may be lost or survive
116      * at random.  
117      * @see java.util.Map#clear()
118      */
119     @Override public void clear() {
120         for (ECMapBucket<K, V> bucket : buckets)
121             bucket.clear();
122     }
123 
124     @SuppressWarnings("unchecked") 
125     @Override public boolean containsKey(Object key) {
126         return getBucket((K) key).containsKey((K) key);
127     }
128 
129     @SuppressWarnings("unchecked") 
130     @Override public boolean containsValue(Object value) {
131         for (ECMapBucket<K, V> bucket : buckets)
132             if(bucket.contains((V) value))
133                 return true;
134         
135         return false;
136     }
137 
138     @Override public Set<java.util.Map.Entry<K, V>> entrySet() {
139         throw new UnsupportedOperationException();
140     }
141 
142     @SuppressWarnings("unchecked") 
143     @Override public V get(Object key) {
144         return getBucket((K) key).get((K) key);
145     }
146 
147     @Override public boolean isEmpty() {
148         for (ECMapBucket<K, V> bucket : buckets)
149             if(bucket.size() > 0)
150                 return false;
151         
152         return true;
153     }
154 
155     @Override public Set<K> keySet() {
156         throw new UnsupportedOperationException();
157     }
158 
159     @Override public V put(K key, V value) {
160         if(key == null)
161             throw new IllegalArgumentException("Key may not be null");
162         if(value == null)
163             throw new IllegalArgumentException("Value may not be null");
164         return getBucket(key).put(key,value);
165     }
166 
167     @Override public void putAll(Map<? extends K, ? extends V> m) {
168         throw new UnsupportedOperationException();
169     }
170 
171     @SuppressWarnings("unchecked") 
172     @Override public V remove(Object key) {
173         return getBucket((K) key).remove((K) key);
174     }
175 
176     @Override public int size() {
177         int size = 0;
178         for (ECMapBucket<K,V> bucket : buckets)
179             size += bucket.size();
180         
181         return size;
182     }
183 
184     @Override public Collection<V> values() {
185         throw new UnsupportedOperationException();
186     }
187 
188 }