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

Last change on this file since 2399 was 2399, checked in by jttt, 14 years ago

Added map of primitives to dataset to make search by id faster
check if primitive already exist in addPrimitive and removePrimitive
use PrimitiveId instead of id + primitive type

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