1 | /* |
2 | * #! |
3 | * Ontopia Engine |
4 | * #- |
5 | * Copyright (C) 2001 - 2013 The Ontopia Project |
6 | * #- |
7 | * Licensed under the Apache License, Version 2.0 (the "License"); |
8 | * you may not use this file except in compliance with the License. |
9 | * You may obtain a copy of the License at |
10 | * |
11 | * http://www.apache.org/licenses/LICENSE-2.0 |
12 | * |
13 | * Unless required by applicable law or agreed to in writing, software |
14 | * distributed under the License is distributed on an "AS IS" BASIS, |
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | * See the License for the specific language governing permissions and |
17 | * limitations under the License. |
18 | * !# |
19 | */ |
20 | |
21 | sclass CompactHashMap<K, V> extends AbstractMap<K, V> { |
22 | final static int INITIAL_SIZE = 3; |
23 | final static double LOAD_FACTOR = 0.6; |
24 | |
25 | // This object is used to represent null, should clients use that as |
26 | final static Object nullObject = new Object(); |
27 | |
28 | /** |
29 | * When a key is deleted this object is put into the hashtable in |
30 | * its place, so that other entries with the same key (collisions) |
31 | * further down the hashtable are not lost after we delete an object |
32 | * in the collision chain. |
33 | */ |
34 | final static Object deletedObject = new Object(); |
35 | int elements; |
36 | int freecells; |
37 | K[] keys; |
38 | V[] values; // object at pos x corresponds to key at pos x |
39 | int modCount; |
40 | |
41 | *() { |
42 | this(INITIAL_SIZE); |
43 | } |
44 | |
45 | *(int size) { |
46 | keys = (K[]) new Object[(size==0 ? 1 : size)]; |
47 | values = (V[]) new Object[(size==0 ? 1 : size)]; |
48 | elements = 0; |
49 | freecells = keys.length; |
50 | modCount = 0; |
51 | } |
52 | |
53 | // ===== MAP IMPLEMENTATION ============================================= |
54 | |
55 | /** |
56 | * Returns the number of key/value mappings in this map. |
57 | */ |
58 | public synchronized int size() { |
59 | return elements; |
60 | } |
61 | |
62 | /** |
63 | * Returns <tt>true</tt> if this map contains no mappings. |
64 | */ |
65 | public synchronized boolean isEmpty() { |
66 | return elements == 0; |
67 | } |
68 | |
69 | /** |
70 | * Removes all key/value mappings in the map. |
71 | */ |
72 | public synchronized void clear() { |
73 | elements = 0; |
74 | for (int ix = 0; ix < keys.length; ix++) { |
75 | keys[ix] = null; |
76 | values[ix] = null; |
77 | } |
78 | freecells = values.length; |
79 | modCount++; |
80 | } |
81 | |
82 | /** |
83 | * Returns <tt>true</tt> if this map contains the specified key. |
84 | */ |
85 | public synchronized boolean containsKey(Object k) { |
86 | return keys[findKeyIndex(k)] != null; |
87 | } |
88 | |
89 | /** |
90 | * Returns <tt>true</tt> if this map contains the specified value. |
91 | */ |
92 | public synchronized boolean containsValue(Object v) { |
93 | if (v == null) |
94 | v = (V)nullObject; |
95 | |
96 | for (int ix = 0; ix < values.length; ix++) |
97 | if (values[ix] != null && values[ix].equals(v)) |
98 | return true; |
99 | |
100 | return false; |
101 | } |
102 | |
103 | /** |
104 | * Returns a read-only set view of the map's keys. |
105 | */ |
106 | public synchronized Set<Entry<K, V>> entrySet() { |
107 | throw new UnsupportedOperationException(); |
108 | } |
109 | |
110 | /** |
111 | * Removes the mapping with key k, if there is one, and returns its |
112 | * value, if there is one, and null if there is none. |
113 | */ |
114 | public synchronized V remove(Object k) { |
115 | int index = findKeyIndex(k); |
116 | |
117 | // we found the right position, now do the removal |
118 | if (keys[index] != null) { |
119 | // we found the object |
120 | |
121 | // same problem here as with put |
122 | V v = values[index]; |
123 | keys[index] = (K) deletedObject; |
124 | values[index] = (V) deletedObject; |
125 | modCount++; |
126 | elements--; |
127 | return v; |
128 | } else |
129 | // we did not find the key |
130 | return null; |
131 | } |
132 | |
133 | /** |
134 | * Adds the specified mapping to this map, returning the old value for |
135 | * the mapping, if there was one. |
136 | */ |
137 | public synchronized V put(K k, V v) { |
138 | if (k == null) |
139 | k = (K)nullObject; |
140 | |
141 | int hash = k.hashCode(); |
142 | int index = (hash & 0x7FFFFFFF) % keys.length; |
143 | int offset = 1; |
144 | int deletedix = -1; |
145 | |
146 | // search for the key (continue while !null and !this key) |
147 | while(keys[index] != null && |
148 | !(keys[index].hashCode() == hash && |
149 | keys[index].equals(k))) { |
150 | |
151 | // if there's a deleted mapping here we can put this mapping here, |
152 | // provided it's not in here somewhere else already |
153 | if (keys[index] == deletedObject) |
154 | deletedix = index; |
155 | |
156 | index = ((index + offset) & 0x7FFFFFFF) % keys.length; |
157 | offset = offset*2 + 1; |
158 | |
159 | if (offset == -1) |
160 | offset = 2; |
161 | } |
162 | |
163 | if (keys[index] == null) { // wasn't present already |
164 | if (deletedix != -1) // reusing a deleted cell |
165 | index = deletedix; |
166 | else |
167 | freecells--; |
168 | |
169 | modCount++; |
170 | elements++; |
171 | |
172 | keys[index] = (K) k; |
173 | values[index] = (V) v; |
174 | |
175 | // rehash with increased capacity |
176 | if (1 - (freecells / (double) keys.length) > LOAD_FACTOR) |
177 | rehash(keys.length*2 + 1); |
178 | return null; |
179 | } else { // was there already |
180 | modCount++; |
181 | V oldv = values[index]; |
182 | values[index] = (V) v; |
183 | return oldv; |
184 | } |
185 | } |
186 | |
187 | /** |
188 | * INTERNAL: Rehashes the hashmap to a bigger size. |
189 | */ |
190 | void rehash(int newCapacity) { |
191 | int oldCapacity = keys.length; |
192 | K[] newKeys = (K[]) new Object[newCapacity]; |
193 | V[] newValues = (V[]) new Object[newCapacity]; |
194 | |
195 | for (int ix = 0; ix < oldCapacity; ix++) { |
196 | Object k = keys[ix]; |
197 | if (k == null || k == deletedObject) |
198 | continue; |
199 | |
200 | int hash = k.hashCode(); |
201 | int index = (hash & 0x7FFFFFFF) % newCapacity; |
202 | int offset = 1; |
203 | |
204 | // search for the key |
205 | while(newKeys[index] != null) { // no need to test for duplicates |
206 | index = ((index + offset) & 0x7FFFFFFF) % newCapacity; |
207 | offset = offset*2 + 1; |
208 | |
209 | if (offset == -1) |
210 | offset = 2; |
211 | } |
212 | |
213 | newKeys[index] = (K) k; |
214 | newValues[index] = values[ix]; |
215 | } |
216 | |
217 | keys = newKeys; |
218 | values = newValues; |
219 | freecells = keys.length - elements; |
220 | } |
221 | |
222 | /** |
223 | * Returns the value for the key k, if there is one, and null if |
224 | * there is none. |
225 | */ |
226 | public synchronized V get(Object k) { |
227 | return values[findKeyIndex(k)]; |
228 | } |
229 | |
230 | /** |
231 | * Returns a virtual read-only collection containing all the values |
232 | * in the map. |
233 | */ |
234 | public synchronized Collection<V> values() { |
235 | return new ValueCollection(); |
236 | } |
237 | |
238 | /** |
239 | * Returns a virtual read-only set of all the keys in the map. |
240 | */ |
241 | public synchronized Set<K> keySet() { |
242 | return new KeySet(); |
243 | } |
244 | |
245 | // --- Internal utilities |
246 | |
247 | final int findKeyIndex(Object k) { |
248 | if (k == null) |
249 | k = nullObject; |
250 | |
251 | int hash = k.hashCode(); |
252 | int index = (hash & 0x7FFFFFFF) % keys.length; |
253 | int offset = 1; |
254 | |
255 | // search for the key (continue while !null and !this key) |
256 | while(keys[index] != null && |
257 | !(keys[index].hashCode() == hash && |
258 | keys[index].equals(k))) { |
259 | index = ((index + offset) & 0x7FFFFFFF) % keys.length; |
260 | offset = offset*2 + 1; |
261 | |
262 | if (offset == -1) |
263 | offset = 2; |
264 | } |
265 | return index; |
266 | } |
267 | |
268 | // --- Key set |
269 | |
270 | class KeySet<K> extends AbstractSet<K> { |
271 | public synchronized int size() { |
272 | return elements; |
273 | } |
274 | |
275 | public synchronized boolean contains(Object k) { |
276 | return containsKey(k); |
277 | } |
278 | |
279 | public synchronized Iterator<K> iterator() { |
280 | return new KeyIterator(); |
281 | } |
282 | } |
283 | |
284 | class KeyIterator<K> implements Iterator<K> { |
285 | private int ix; |
286 | |
287 | private KeyIterator() { |
288 | // walk up to first value, so that hasNext() and next() return |
289 | // correct results |
290 | for (; ix < keys.length; ix++) |
291 | if (values[ix] != null && keys[ix] != deletedObject) |
292 | break; |
293 | } |
294 | |
295 | public synchronized boolean hasNext() { |
296 | return ix < keys.length; |
297 | } |
298 | |
299 | public synchronized void remove() { |
300 | throw new UnsupportedOperationException("Collection is read-only"); |
301 | } |
302 | |
303 | public synchronized K next() { |
304 | if (ix >= keys.length) |
305 | throw new NoSuchElementException(); |
306 | K key = (K) keys[ix++]; |
307 | |
308 | // walk up to next value |
309 | for (; ix < keys.length; ix++) |
310 | if (keys[ix] != null && keys[ix] != deletedObject) |
311 | break; |
312 | |
313 | // ix now either points to next key, or outside array (if no next) |
314 | return key; |
315 | } |
316 | } |
317 | |
318 | // --- Value collection |
319 | |
320 | class ValueCollection<V> extends AbstractCollection<V> { |
321 | public synchronized int size() { |
322 | return elements; |
323 | } |
324 | |
325 | public synchronized Iterator<V> iterator() { |
326 | return new ValueIterator(); |
327 | } |
328 | |
329 | public synchronized boolean contains(Object v) { |
330 | return containsValue(v); |
331 | } |
332 | } |
333 | |
334 | class ValueIterator<V> implements Iterator<V> { |
335 | private int ix; |
336 | |
337 | private ValueIterator() { |
338 | // walk up to first value, so that hasNext() and next() return |
339 | // correct results |
340 | for (; ix < values.length; ix++) |
341 | if (values[ix] != null && values[ix] != deletedObject) |
342 | break; |
343 | } |
344 | |
345 | public synchronized boolean hasNext() { |
346 | return ix < values.length; |
347 | } |
348 | |
349 | public synchronized void remove() { |
350 | throw new UnsupportedOperationException("Collection is read-only"); |
351 | } |
352 | |
353 | public synchronized V next() { |
354 | if (ix >= values.length) |
355 | throw new NoSuchElementException(); |
356 | V value = (V) values[ix++]; |
357 | |
358 | // walk up to next value |
359 | for (; ix < values.length; ix++) |
360 | if (values[ix] != null && values[ix] != deletedObject) |
361 | break; |
362 | |
363 | // ix now either points to next value, or outside array (if no next) |
364 | return value; |
365 | } |
366 | } |
367 | } |
Began life as a copy of #1012355
download show line numbers debug dex old transpilations
Travelled to 13 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #1013303 |
Snippet name: | CompactHashMap - synchronized |
Eternal ID of this version: | #1013303/3 |
Text MD5: | 8356892939b7988d6cb4b6a823d16628 |
Author: | stefan |
Category: | javax / collections |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2017-12-31 19:41:35 |
Source code size: | 9748 bytes / 367 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 420 / 885 |
Version history: | 2 change(s) |
Referenced in: | [show references] |