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