001 /*
002 * SimpleMapImpl.java
003 *
004 * Copyright (c) 2001, The University of Sheffield.
005 *
006 * This file is part of GATE (see http://gate.ac.uk/), and is free
007 * software, licenced under the GNU Library General Public License,
008 * Version 2, June 1991 (in the distribution as file licence.html,
009 * and also available at http://gate.ac.uk/gate/licence.html).
010 *
011 * D.Ognyanoff, 5/Nov/2001
012 *
013 * $Id: SimpleMapImpl.java 13144 2010-10-14 14:32:07Z valyt $
014 */
015
016 package gate.util;
017
018 import java.io.IOException;
019 import java.io.ObjectInputStream;
020 import java.io.Serializable;
021 import java.util.*;
022 import java.util.concurrent.ConcurrentHashMap;
023
024 /**
025 * Implements Map interface in using less memory. Very simple but usefull
026 * for small number of items on it.
027 */
028
029 class SimpleMapImpl implements Map<Object, Object>,
030 java.lang.Cloneable, java.io.Serializable {
031
032 /**
033 * Special marker class used to represent null in the keys array.
034 */
035 private static class NullKey implements Serializable {
036 private static final long serialVersionUID = 6391916290867211345L;
037
038 private NullKey() {
039 }
040 }
041
042 /**
043 * The capacity of the map
044 */
045 int capacity = 3;
046
047 /**
048 * The current number of elements of the map
049 */
050 int count = 0;
051
052 /**
053 * Array keeping the keys of the entries in the map. It is "synchrnized"
054 * with the values array - the Nth position in both arrays correspond
055 * to one and the same entry
056 */
057 Object theKeys[];
058
059 /**
060 * Array keeping the values of the entries in the map. It is "synchrnized"
061 * with the keys array - the Nth position in both arrays correspond
062 * to one and the same entry
063 */
064 Object theValues[];
065
066 /** Freeze the serialization UID. */
067 static final long serialVersionUID = -6747241616127229116L;
068
069 /** the Object instance that will represent the NULL keys in the map */
070 transient static Object nullKey = new NullKey();
071
072 /** the static 'all keys' collection */
073 transient public static ConcurrentHashMap theKeysHere;
074
075 /** additional static members initialization */
076 static {
077 theKeysHere = new ConcurrentHashMap();
078 theKeysHere.put(nullKey, nullKey);
079 } // static code
080
081 /**
082 * Constructor
083 */
084 public SimpleMapImpl() {
085 theKeys = new Object[capacity];
086 theValues = new Object[capacity];
087 } // SimpleMapImpl()
088
089 /**
090 * return the number of elements in the map
091 */
092 public int size() {
093 return count;
094 } // size()
095
096 /**
097 * return true if there are no elements in the map
098 */
099 public boolean isEmpty() {
100 return (count == 0);
101 } // isEmpty()
102
103 /**
104 * Not supported. This method is here only to conform the Map interface
105 */
106 public Collection values() {
107 throw new UnsupportedOperationException(
108 "SimpleMapImpl.values() not implemented!");
109 } // values()
110
111 /**
112 * return the set of the keys in the map. The changes in the set DO NOT
113 * affect the map.
114 */
115 public Set keySet()
116 {
117 HashSet s = new HashSet(size());
118 Object k;
119 for (int i = 0; i < count; i++) {
120 k = theKeys[i];
121 if (k == nullKey)
122 s.add(null);
123 else
124 s.add(k);
125 } //for
126 return s;
127 } // keySet()
128
129 /**
130 * clear the map
131 */
132 public void clear()
133 {
134 for (int i = 0; i < count; i++) {
135 theKeys[i] = null;
136 theValues[i] = null;
137 } // for
138 count = 0;
139 } // clear
140
141 /**
142 * return true if the key is in the map
143 */
144 public boolean containsKey(Object key) {
145 return (getPostionByKey(key) != -1);
146 }// containsKey
147
148 /**
149 * return true if the map contains that value
150 */
151 public boolean containsValue(Object value) {
152 return (getPostionByValue(value) != -1);
153 }// containsValue
154
155 /**
156 * return the value associated with the key. If the key is
157 * not in the map returns null.
158 */
159 public Object get(Object key) {
160 int pos = getPostionByKey(key);
161 return (pos == -1) ? null : theValues[pos];
162 } // get
163
164 /**
165 * put a value in the map using the given key. If the key exist in the map
166 * the value is replaced and the old one is returned.
167 */
168 public Object put(Object key, Object value) {
169 Object gKey;
170 if (key == null) {
171 key = nullKey;
172 gKey = nullKey;
173 } else
174 gKey = theKeysHere.putIfAbsent(key, key);
175 // if the key is already in the 'all keys' map - try to find it in that instance
176 // comparing by reference
177 if (gKey != null) {
178 for (int i = 0; i < count; i++) {
179 if (gKey == theKeys[i]) {
180 // we found the reference - return the value
181 Object oldVal = theValues[i];
182 theValues[i] = value;
183 return oldVal;
184 }
185 } // for
186 } else {// if(gKey != null)
187 // no, the key is not in the 'all keys' map - put it there
188 gKey = key;
189 }
190 // enlarge the containers if necessary
191 if (count == capacity)
192 increaseCapacity();
193
194 // put the key and value to the map
195 theKeys[count] = gKey;
196 theValues[count] = value;
197 count++;
198 return null;
199 } // put
200
201 /**
202 * remove value from the map using it's key.
203 */
204 public Object remove(Object key) {
205 int pos = getPostionByKey(key);
206 if (pos == -1)
207 return null;
208
209 // save the value to return it at the end
210 Object oldVal = theValues[pos];
211 count--;
212 // move the last element key and value removing the element
213 if (count != 0) {
214 theKeys[pos] = theKeys[count];
215 theValues[pos] = theValues[count];
216 }
217 // clear the last position
218 theKeys[count] = null;
219 theValues[count] = null;
220
221 // return the value
222 return oldVal;
223 } // remove
224
225 /**
226 * put all the elements from a map
227 */
228 public void putAll(Map t)
229 {
230 if (t == null) {
231 throw new UnsupportedOperationException(
232 "SimpleMapImpl.putAll argument is null");
233 } // if (t == null)
234
235 if (t instanceof SimpleMapImpl) {
236 SimpleMapImpl sfm = (SimpleMapImpl)t;
237 Object key;
238 for (int i = 0; i < sfm.count; i++) {
239 key = sfm.theKeys[i];
240 put(key, sfm.theValues[i]);
241 } //for
242 } else { // if (t instanceof SimpleMapImpl)
243 Iterator entries = t.entrySet().iterator();
244 Map.Entry e;
245 while (entries.hasNext()) {
246 e = (Map.Entry)entries.next();
247 put(e.getKey(), e.getValue());
248 } // while
249 } // if(t instanceof SimpleMapImpl)
250 } // putAll
251
252 /**
253 * return positive value as index of the key in the map.
254 * Negative value means that the key is not present in the map
255 */
256 private int getPostionByKey(Object key) {
257 if (key == null)
258 key = nullKey;
259 // check the 'all keys' map for the very first key occurence
260 key = theKeysHere.get(key);
261 if (key == null)
262 return -1;
263
264 for (int i = 0; i < count; i++) {
265 if (key == theKeys[i])
266 return i;
267 } // for
268 return -1;
269 } // getPostionByKey
270
271 /**
272 * return the index of the key in the map comparing them by reference only.
273 * This method is used in subsume check to speed it up.
274 */
275 protected int getSubsumeKey(Object key) {
276 for (int i = 0; i < count; i++) {
277 if (key == theKeys[i])
278 return i;
279 } // for
280 return -1;
281 } // getPostionByKey
282
283 /**
284 * return positive value as index of the value in the map.
285 */
286 private int getPostionByValue(Object value) {
287 Object av;
288 for (int i = 0; i < count; i++) {
289 av = theValues[i];
290 if (value == null) {
291 if (av == null)
292 return i;
293 } else {//if (value == null)
294 if (value.equals(av))
295 return i;
296 } //if (value == null)
297 } // for
298
299 return -1;
300 } // getPostionByValue
301
302 // Modification Operations
303 private void increaseCapacity() {
304 int oldCapacity = capacity;
305 capacity *= 2;
306 Object oldKeys[] = theKeys;
307 theKeys = new Object[capacity];
308
309 Object oldValues[] = theValues;
310 theValues = new Object[capacity];
311
312 System.arraycopy(oldKeys, 0, theKeys, 0, oldCapacity);
313 System.arraycopy(oldValues, 0, theValues, 0, oldCapacity);
314 } // increaseCapacity
315
316 /**
317 * Auxiliary classes needed for the support of entrySet() method
318 */
319 private static class Entry implements Map.Entry {
320 int hash;
321 Object key;
322 Object value;
323
324 Entry(int hash, Object key, Object value) {
325 this.hash = hash;
326 this.key = key;
327 this.value = value;
328 }
329
330 protected Object clone() {
331 return new Entry(hash, key, value);
332 }
333
334 public Object getKey() {
335 return key;
336 }
337
338 public Object getValue() {
339 return value;
340 }
341
342 public Object setValue(Object value) {
343 Object oldValue = this.value;
344 this.value = value;
345 return oldValue;
346 }
347
348 public boolean equals(Object o) {
349 if (!(o instanceof Map.Entry))
350 return false;
351 Map.Entry e = (Map.Entry)o;
352
353 return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
354 (value==null ? e.getValue()==null : value.equals(e.getValue()));
355 }
356
357 public int hashCode() {
358 return hash ^ (key==null ? 0 : key.hashCode());
359 }
360
361 public String toString() {
362 return key+"="+value;
363 }
364 } // Entry
365
366
367 public Set entrySet() {
368 HashSet s = new HashSet(size());
369 Object v, k;
370 for (int i = 0; i < count; i++) {
371 k = theKeys[i];
372 s.add(new Entry(k.hashCode(), ((k==nullKey)?null:k), theValues[i]));
373 } //for
374 return s;
375 } // entrySet
376
377 // Comparison and hashing
378 public boolean equals(Object o) {
379 if (!(o instanceof Map)) {
380 return false;
381 }
382
383 Map m = (Map)o;
384 if (m.size() != count) {
385 return false;
386 }
387
388 Object v, k;
389 for (int i = 0; i < count; i++) {
390 k = theKeys[i];
391 v = m.get(k);
392 if (v==null) {
393 if (theValues[i]!=null)
394 return false;
395 }
396 else if (!v.equals(theValues[i])){
397 return false;
398 }
399 } // for
400
401 return true;
402 } // equals
403
404 /**
405 * return the hashCode for the map
406 */
407 public int hashCode() {
408 int h = 0;
409 Iterator i = entrySet().iterator();
410 while (i.hasNext())
411 h += i.next().hashCode();
412 return h;
413 } // hashCode
414
415 /**
416 * Create a copy of the map including the data.
417 */
418 public Object clone() {
419 SimpleMapImpl newMap;
420 try {
421 newMap = (SimpleMapImpl)super.clone();
422 } catch (CloneNotSupportedException e) {
423 throw(new InternalError(e.toString()));
424 }
425
426 newMap.count = count;
427 newMap.theKeys = new Object[capacity];
428 System.arraycopy(theKeys, 0, newMap.theKeys, 0, capacity);
429
430 newMap.theValues = new Object[capacity];
431 System.arraycopy(theValues, 0, newMap.theValues, 0, capacity);
432
433 return newMap;
434 } // clone
435
436 public String toString() {
437 int max = size() - 1;
438 StringBuffer buf = new StringBuffer();
439 Iterator i = entrySet().iterator();
440
441 buf.append("{");
442 for (int j = 0; j <= max; j++) {
443 Entry e = (Entry) (i.next());
444 buf.append(e.getKey() + "=" + e.getValue());
445 if (j < max)
446 buf.append(", ");
447 }
448 buf.append("}");
449 return buf.toString();
450 } // toString
451
452 /**
453 * readObject - calls the default readObject() and then initialises the
454 * transient data
455 *
456 * @serialData Read serializable fields. No optional data read.
457 */
458 private void readObject(ObjectInputStream s)
459 throws IOException, ClassNotFoundException {
460 s.defaultReadObject();
461 if (theKeysHere == null) {
462 synchronized(SimpleMapImpl.class) {
463 theKeysHere = new ConcurrentHashMap();
464 theKeysHere.put(nullKey, nullKey);
465 }
466 }
467 for (int i = 0; i < theKeys.length; i++) {
468 if(theKeys[i] instanceof NullKey) {
469 theKeys[i] = nullKey;
470 }
471 else if(theKeys[i] != null) {
472 // check if the key is in the 'all keys' map, adding it if not
473 Object o = theKeysHere.putIfAbsent(theKeys[i], theKeys[i]);
474 if (o != null) // yes - so reuse the reference
475 theKeys[i] = o;
476 }
477 }//for
478 }//readObject
479 } //SimpleMapImpl
|