source: josm/trunk/src/org/openstreetmap/josm/data/osm/Storage.java@ 3453

Last change on this file since 3453 was 3453, checked in by bastiK, 14 years ago

suppress some trivial warnings

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/plain
File size: 13.1 KB
Line 
1/*
2 * JOSMng - a Java Open Street Map editor, the next generation.
3 *
4 * Copyright (C) 2008 Petr Nejedly <P.Nejedly@sh.cvut.cz>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21package org.openstreetmap.josm.data.osm;
22
23import java.util.AbstractSet;
24import java.util.Collection;
25import java.util.ConcurrentModificationException;
26import java.util.Iterator;
27import java.util.Map;
28import java.util.NoSuchElementException;
29import java.util.Set;
30
31/**
32 * A Set-like class that allows looking up equivalent preexising instance.
33 * It is useful whereever one would use self-mapping construct like
34 * <code>Map<T,T>.put(t,t), that is, for caches, uniqueness filters or similar.
35 *
36 * The semantics of equivalency can be external to the object, using the
37 * {@link Hash} interface. The set also supports querying for entries using
38 * different key type, in case you can provide a Hash implementation
39 * that can resolve the equality.
40 *
41 * <h2>Examples</h2>
42 * <ul><li>A String cache:
43 * <pre>
44 * Storage<String> cache = new Storage(); // use default Hash
45 * for (String input : data) {
46 * String onlyOne = cache.putIfUnique(input);
47 * ....
48 * }
49 * </pre></li>
50 * <li>Identity-based set:
51 * <pre>
52 * Storage<Object> identity = new Storage(new Hash<Object,Object> {
53 * public int getHashCode(Object o) {
54 * return System.identityHashCode(o);
55 * }
56 * public boolean equals(Object o1, Object o2) {
57 * return o1 == o2;
58 * }
59 * });
60 * </pre></li>
61 * <li>An object with int ID and id-based lookup:
62 * <pre>
63 * class Thing { int id; }
64 * Storage<Thing> things = new Storage(new Hash<Thing,Thing>() {
65 * public int getHashCode(Thing t) {
66 * return t.id;
67 * }
68 * public boolean equals(Thing t1, Thing t2) {
69 * return t1 == t2;
70 * }
71 * });
72 * Map<Integer,Thing> fk = things.foreignKey(new Hash<Integer,Thing>() {
73 * public int getHashCode(Integer i) {
74 * return i.getIntValue();
75 * }
76 * public boolean equals(Integer k, Thing t) {
77 * return t.id == k.getIntvalue();
78 * }
79 * }
80 *
81 * things.put(new Thing(3));
82 * assert things.get(new Thing(3)) == fk.get(3);
83 * </pre></li>
84 *
85 *
86 * @author nenik
87 */
88public class Storage<T> extends AbstractSet<T> {
89 private final Hash<? super T,? super T> hash;
90 private T[] data;
91 private int mask;
92 private int size;
93 private transient volatile int modCount = 0;
94 private float loadFactor = 0.6f;
95 private final boolean safeIterator;
96 private boolean arrayCopyNecessary;
97
98 public Storage() {
99 this(Storage.<T>defaultHash());
100 }
101
102 public Storage(int capacity) {
103 this(Storage.<T>defaultHash(), capacity, false);
104 }
105
106 public Storage(Hash<? super T,? super T> ha) {
107 this(ha, 16, false);
108 }
109
110 public Storage(Hash<? super T, ? super T> ha, int capacity, boolean safeIterator) {
111 this.hash = ha;
112 int cap = 1 << (int)(Math.ceil(Math.log(capacity/loadFactor) / Math.log(2)));
113 @SuppressWarnings("unchecked") T[] newData = (T[]) new Object[cap];
114 data = newData;
115 mask = data.length - 1;
116 this.safeIterator = safeIterator;
117 }
118
119 private void copyArray() {
120 if (arrayCopyNecessary) {
121 @SuppressWarnings("unchecked") T[] newData = (T[]) new Object[data.length];
122 System.arraycopy(data, 0, newData, 0, data.length);
123 data = newData;
124 arrayCopyNecessary = false;
125 }
126 }
127
128 // --------------- Collection implementation ------------------------
129 @Override
130 public synchronized int size() {
131 return size;
132 }
133
134 @Override
135 public synchronized Iterator<T> iterator() {
136 if (safeIterator) {
137 arrayCopyNecessary = true;
138 return new SafeReadonlyIter(data);
139 } else
140 return new Iter();
141
142 }
143
144 @Override
145 public synchronized boolean contains(Object o) {
146 @SuppressWarnings("unchecked") T t = (T) o;
147 int bucket = getBucket(hash, t);
148 return bucket >= 0;
149 }
150
151 @Override
152 public synchronized boolean add(T t) {
153 T orig = putUnique(t);
154 return orig == t;
155 }
156
157 @Override
158 public synchronized boolean remove(Object o) {
159 @SuppressWarnings("unchecked") T t = (T) o;
160 T tOrig = removeElem(t);
161 return tOrig != null;
162 }
163
164 @Override
165 public synchronized void clear() {
166 copyArray();
167 modCount++;
168 size = 0;
169 for (int i = 0; i<data.length; i++) {
170 data[i] = null;
171 }
172 }
173
174 @Override
175 public synchronized int hashCode() {
176 int h = 0;
177 for (T t : this) {
178 h += hash.getHashCode(t);
179 }
180 return h;
181 }
182
183 // ----------------- Extended API ----------------------------
184
185 public synchronized T put(T t) {
186 copyArray();
187 modCount++;
188 ensureSpace();
189
190 int bucket = getBucket(hash, t);
191 if (bucket < 0) {
192 size++;
193 bucket = ~bucket;
194 assert data[bucket] == null;
195 }
196
197 T old = data[bucket];
198 data[bucket] = t;
199
200 return old;
201 }
202
203 public synchronized T get(T t) {
204 int bucket = getBucket(hash, t);
205 return bucket < 0 ? null : data[bucket];
206 }
207
208 public synchronized T putUnique(T t) {
209 copyArray();
210 modCount++;
211 ensureSpace();
212
213 int bucket = getBucket(hash, t);
214 if (bucket < 0) { // unique
215 size++;
216 assert data[~bucket] == null;
217 data[~bucket] = t;
218 return t;
219 }
220
221 return data[bucket];
222 }
223
224 public synchronized T removeElem(T t) {
225 copyArray();
226 modCount++;
227 int bucket = getBucket(hash, t);
228 return bucket < 0 ? null : doRemove(bucket);
229 }
230
231 public <K> Map<K,T> foreignKey(Hash<K,? super T> h) {
232 return new FMap<K>(h);
233 }
234
235 // ---------------- Implementation
236
237 /**
238 * Additional mixing of hash
239 */
240 private int rehash(int h) {
241 //return 54435761*h;
242 return 1103515245*h >> 2;
243 }
244
245 /**
246 * Finds a bucket for given key.
247 *
248 * @param key The key to compare
249 * @return the bucket equivalent to the key or -(bucket) as an empty slot
250 * where such an entry can be stored.
251 */
252 private <K> int getBucket(Hash<K,? super T> ha, K key) {
253 T entry;
254 int hcode = rehash(ha.getHashCode(key));
255 int bucket = hcode & mask;
256 while ((entry = data[bucket]) != null) {
257 if (ha.equals(key, entry))
258 return bucket;
259 bucket = (bucket+1) & mask;
260 }
261 return ~bucket;
262 }
263
264 private T doRemove(int slot) {
265 T t = data[slot];
266 assert t != null;
267
268 fillTheHole(slot); // fill the hole (or null it)
269 size--;
270 return t;
271 }
272
273 private void fillTheHole(int hole) {
274 int bucket = (hole+1) & mask;
275 T entry;
276
277 while ((entry = data[bucket]) != null) {
278 int right = rehash(hash.getHashCode(entry)) & mask;
279 // if the entry should be in <hole+1,bucket-1> (circular-wise)
280 // we can't move it. The move can be proved safe otherwise,
281 // because the entry safely belongs to <previous_null+1,hole>
282 if ((bucket < right && (right <= hole || hole <= bucket)) ||
283 (right <=hole && hole <= bucket)) {
284
285 data[hole] = data[bucket];
286 hole = bucket;
287 }
288 bucket = (bucket+1) & mask;
289 }
290
291 // no entry belongs here, just null out the slot
292 data[hole] = null;
293 }
294
295 private void ensureSpace() {
296 if (size > data.length*loadFactor) { // rehash
297 @SuppressWarnings("unchecked") T[] big = (T[]) new Object[data.length * 2];
298 int nMask = big.length - 1;
299
300 for (T o : data) {
301 if (o == null) {
302 continue;
303 }
304 int bucket = rehash(hash.getHashCode(o)) & nMask;
305 while (big[bucket] != null) {
306 bucket = (bucket+1) & nMask;
307 }
308 big[bucket] = o;
309 }
310
311 data = big;
312 mask = nMask;
313 }
314 }
315
316 // -------------- factories --------------------
317 /**
318 * A factory for default hash implementation.
319 * @return a hash implementation that just delegates to object's own
320 * hashCode and equals.
321 */
322 public static <O> Hash<O,O> defaultHash() {
323 return new Hash<O,O>() {
324 public int getHashCode(O t) {
325 return t.hashCode();
326 }
327 public boolean equals(O t1, O t2) {
328 return t1.equals(t2);
329 }
330 };
331 }
332 /*
333 public static <O> Hash<O,O> identityHash() {
334 return new Hash<O,O>() {
335 public int getHashCode(O t) {
336 return System.identityHashCode(t);
337 }
338 public boolean equals(O t1, O t2) {
339 return t1 == t2;
340 }
341 };
342 }
343 */
344
345 private class FMap<K> implements Map<K,T> {
346 Hash<K,? super T> fHash;
347
348 private FMap(Hash<K,? super T> h) {
349 fHash = h;
350 }
351
352 public int size() {
353 return Storage.this.size();
354 }
355
356 public boolean isEmpty() {
357 return Storage.this.isEmpty();
358 }
359
360 public boolean containsKey(Object o) {
361 @SuppressWarnings("unchecked") K key = (K) o;
362 int bucket = getBucket(fHash, key);
363 return bucket >= 0;
364 }
365
366 public boolean containsValue(Object value) {
367 return Storage.this.contains(value);
368 }
369
370 public T get(Object o) {
371 @SuppressWarnings("unchecked") K key = (K) o;
372 int bucket = getBucket(fHash, key);
373 return bucket < 0 ? null : data[bucket];
374 }
375
376 public T put(K key, T value) {
377 if (!fHash.equals(key, value)) throw new IllegalArgumentException("inconsistent key");
378 return Storage.this.put(value);
379 }
380
381 public T remove(Object o) {
382 modCount++;
383 @SuppressWarnings("unchecked") K key = (K) o;
384 int bucket = getBucket(fHash, key);
385
386 return bucket < 0 ? null : doRemove(bucket);
387 }
388
389 public void putAll(Map<? extends K, ? extends T> m) {
390 if (m instanceof Storage.FMap) {
391 Storage.this.addAll(((Storage.FMap)m).values());
392 } else {
393 for (Map.Entry<? extends K, ? extends T> e : m.entrySet()) {
394 put(e.getKey(), e.getValue());
395 }
396 }
397 }
398
399 public void clear() {
400 Storage.this.clear();
401 }
402
403 public Set<K> keySet() {
404 throw new UnsupportedOperationException();
405 }
406
407 public Collection<T> values() {
408 return Storage.this;
409 }
410
411 public Set<Entry<K, T>> entrySet() {
412 throw new UnsupportedOperationException();
413 }
414 }
415
416 private final class SafeReadonlyIter implements Iterator<T> {
417 final T[] data;
418 int slot = 0;
419
420 SafeReadonlyIter(T[] data) {
421 this.data = data;
422 }
423
424 public boolean hasNext() {
425 align();
426 return slot < data.length;
427 }
428
429 public T next() {
430 if (!hasNext()) throw new NoSuchElementException();
431 return data[slot++];
432 }
433
434 public void remove() {
435 throw new UnsupportedOperationException();
436 }
437
438 private void align() {
439 while (slot < data.length && data[slot] == null) {
440 slot++;
441 }
442 }
443 }
444
445
446 private final class Iter implements Iterator<T> {
447 private final int mods;
448 int slot = 0;
449 int removeSlot = -1;
450
451 Iter() {
452 mods = modCount;
453 }
454
455 public boolean hasNext() {
456 align();
457 return slot < data.length;
458 }
459
460 public T next() {
461 if (!hasNext()) throw new NoSuchElementException();
462 removeSlot = slot;
463 return data[slot++];
464 }
465
466 public void remove() {
467 if (removeSlot == -1) throw new IllegalStateException();
468
469 doRemove(removeSlot);
470 slot = removeSlot; // some entry might have been relocated here
471 removeSlot = -1;
472 }
473
474 private void align() {
475 if (mods != modCount)
476 throw new ConcurrentModificationException();
477 while (slot < data.length && data[slot] == null) {
478 slot++;
479 }
480 }
481 }
482
483}
Note: See TracBrowser for help on using the repository browser.