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

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

reduce number of warnings

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/plain
File size: 12.8 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 data = (T[]) new Object[cap];
114 mask = data.length - 1;
115 this.safeIterator = safeIterator;
116 }
117
118 private void copyArray() {
119 if (arrayCopyNecessary) {
120 T[] newData = (T[]) new Object[data.length];
121 System.arraycopy(data, 0, newData, 0, data.length);
122 data = newData;
123 arrayCopyNecessary = false;
124 }
125 }
126
127 // --------------- Collection implementation ------------------------
128 @Override
129 public synchronized int size() {
130 return size;
131 }
132
133 @Override
134 public synchronized Iterator<T> iterator() {
135 if (safeIterator) {
136 arrayCopyNecessary = true;
137 return new SafeReadonlyIter(data);
138 } else
139 return new Iter();
140
141 }
142
143 @Override
144 public synchronized boolean contains(Object o) {
145 int bucket = getBucket(hash, (T)o);
146 return bucket >= 0;
147 }
148
149 @Override
150 public synchronized boolean add(T t) {
151 T orig = putUnique(t);
152 return orig == t;
153 }
154
155 @Override
156 public synchronized boolean remove(Object o) {
157 T orig = removeElem((T)o);
158 return orig != null;
159 }
160
161 @Override
162 public synchronized void clear() {
163 copyArray();
164 modCount++;
165 size = 0;
166 for (int i = 0; i<data.length; i++) {
167 data[i] = null;
168 }
169 }
170
171 @Override
172 public synchronized int hashCode() {
173 int h = 0;
174 for (T t : this) {
175 h += hash.getHashCode(t);
176 }
177 return h;
178 }
179
180 // ----------------- Extended API ----------------------------
181
182 public synchronized T put(T t) {
183 copyArray();
184 modCount++;
185 ensureSpace();
186
187 int bucket = getBucket(hash, t);
188 if (bucket < 0) {
189 size++;
190 bucket = ~bucket;
191 assert data[bucket] == null;
192 }
193
194 T old = data[bucket];
195 data[bucket] = t;
196
197 return old;
198 }
199
200 public synchronized T get(T t) {
201 int bucket = getBucket(hash, t);
202 return bucket < 0 ? null : data[bucket];
203 }
204
205 public synchronized T putUnique(T t) {
206 copyArray();
207 modCount++;
208 ensureSpace();
209
210 int bucket = getBucket(hash, t);
211 if (bucket < 0) { // unique
212 size++;
213 assert data[~bucket] == null;
214 data[~bucket] = t;
215 return t;
216 }
217
218 return data[bucket];
219 }
220
221 public synchronized T removeElem(T t) {
222 copyArray();
223 modCount++;
224 int bucket = getBucket(hash, t);
225 return bucket < 0 ? null : doRemove(bucket);
226 }
227
228 public <K> Map<K,T> foreignKey(Hash<K,? super T> h) {
229 return new FMap<K>(h);
230 }
231
232 // ---------------- Implementation
233
234 /**
235 * Additional mixing of hash
236 */
237 private int rehash(int h) {
238 //return 54435761*h;
239 return 1103515245*h >> 2;
240 }
241
242 /**
243 * Finds a bucket for given key.
244 *
245 * @param key The key to compare
246 * @return the bucket equivalent to the key or -(bucket) as an empty slot
247 * where such an entry can be stored.
248 */
249 private <K> int getBucket(Hash<K,? super T> ha, K key) {
250 T entry;
251 int hcode = rehash(ha.getHashCode(key));
252 int bucket = hcode & mask;
253 while ((entry = data[bucket]) != null) {
254 if (ha.equals(key, entry))
255 return bucket;
256 bucket = (bucket+1) & mask;
257 }
258 return ~bucket;
259 }
260
261 private T doRemove(int slot) {
262 T t = data[slot];
263 assert t != null;
264
265 fillTheHole(slot); // fill the hole (or null it)
266 size--;
267 return t;
268 }
269
270 private void fillTheHole(int hole) {
271 int bucket = (hole+1) & mask;
272 T entry;
273
274 while ((entry = data[bucket]) != null) {
275 int right = rehash(hash.getHashCode(entry)) & mask;
276 // if the entry should be in <hole+1,bucket-1> (circular-wise)
277 // we can't move it. The move can be proved safe otherwise,
278 // because the entry safely belongs to <previous_null+1,hole>
279 if ((bucket < right && (right <= hole || hole <= bucket)) ||
280 (right <=hole && hole <= bucket)) {
281
282 data[hole] = data[bucket];
283 hole = bucket;
284 }
285 bucket = (bucket+1) & mask;
286 }
287
288 // no entry belongs here, just null out the slot
289 data[hole] = null;
290 }
291
292 private void ensureSpace() {
293 if (size > data.length*loadFactor) { // rehash
294 T[] big = (T[]) new Object[data.length * 2];
295 int nMask = big.length - 1;
296
297 for (T o : data) {
298 if (o == null) {
299 continue;
300 }
301 int bucket = rehash(hash.getHashCode(o)) & nMask;
302 while (big[bucket] != null) {
303 bucket = (bucket+1) & nMask;
304 }
305 big[bucket] = o;
306 }
307
308 data = big;
309 mask = nMask;
310 }
311 }
312
313 // -------------- factories --------------------
314 /**
315 * A factory for default hash implementation.
316 * @return a hash implementation that just delegates to object's own
317 * hashCode and equals.
318 */
319 public static <O> Hash<O,O> defaultHash() {
320 return new Hash<O,O>() {
321 public int getHashCode(O t) {
322 return t.hashCode();
323 }
324 public boolean equals(O t1, O t2) {
325 return t1.equals(t2);
326 }
327 };
328 }
329 /*
330 public static <O> Hash<O,O> identityHash() {
331 return new Hash<O,O>() {
332 public int getHashCode(O t) {
333 return System.identityHashCode(t);
334 }
335 public boolean equals(O t1, O t2) {
336 return t1 == t2;
337 }
338 };
339 }
340 */
341
342 private class FMap<K> implements Map<K,T> {
343 Hash<K,? super T> fHash;
344
345 private FMap(Hash<K,? super T> h) {
346 fHash = h;
347 }
348
349 public int size() {
350 return Storage.this.size();
351 }
352
353 public boolean isEmpty() {
354 return Storage.this.isEmpty();
355 }
356
357 public boolean containsKey(Object key) {
358 int bucket = getBucket(fHash, (K)key);
359 return bucket >= 0;
360 }
361
362 public boolean containsValue(Object value) {
363 return Storage.this.contains(value);
364 }
365
366 public T get(Object key) {
367 int bucket = getBucket(fHash, (K)key);
368 return bucket < 0 ? null : data[bucket];
369 }
370
371 public T put(K key, T value) {
372 if (!fHash.equals(key, value)) throw new IllegalArgumentException("inconsistent key");
373 return Storage.this.put(value);
374 }
375
376 public T remove(Object key) {
377 modCount++;
378 int bucket = getBucket(fHash,(K)key);
379
380 return bucket < 0 ? null : doRemove(bucket);
381 }
382
383 public void putAll(Map<? extends K, ? extends T> m) {
384 if (m instanceof Storage.FMap) {
385 Storage.this.addAll(((Storage.FMap)m).values());
386 } else {
387 for (Map.Entry<? extends K, ? extends T> e : m.entrySet()) {
388 put(e.getKey(), e.getValue());
389 }
390 }
391 }
392
393 public void clear() {
394 Storage.this.clear();
395 }
396
397 public Set<K> keySet() {
398 throw new UnsupportedOperationException();
399 }
400
401 public Collection<T> values() {
402 return Storage.this;
403 }
404
405 public Set<Entry<K, T>> entrySet() {
406 throw new UnsupportedOperationException();
407 }
408 }
409
410 private final class SafeReadonlyIter implements Iterator<T> {
411 final T[] data;
412 int slot = 0;
413
414 SafeReadonlyIter(T[] data) {
415 this.data = data;
416 }
417
418 public boolean hasNext() {
419 align();
420 return slot < data.length;
421 }
422
423 public T next() {
424 if (!hasNext()) throw new NoSuchElementException();
425 return data[slot++];
426 }
427
428 public void remove() {
429 throw new UnsupportedOperationException();
430 }
431
432 private void align() {
433 while (slot < data.length && data[slot] == null) {
434 slot++;
435 }
436 }
437 }
438
439
440 private final class Iter implements Iterator<T> {
441 private final int mods;
442 int slot = 0;
443 int removeSlot = -1;
444
445 Iter() {
446 mods = modCount;
447 }
448
449 public boolean hasNext() {
450 align();
451 return slot < data.length;
452 }
453
454 public T next() {
455 if (!hasNext()) throw new NoSuchElementException();
456 removeSlot = slot;
457 return data[slot++];
458 }
459
460 public void remove() {
461 if (removeSlot == -1) throw new IllegalStateException();
462
463 doRemove(removeSlot);
464 slot = removeSlot; // some entry might have been relocated here
465 removeSlot = -1;
466 }
467
468 private void align() {
469 if (mods != modCount)
470 throw new ConcurrentModificationException();
471 while (slot < data.length && data[slot] == null) {
472 slot++;
473 }
474 }
475 }
476
477}
Note: See TracBrowser for help on using the repository browser.