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