Libraryless. Click here for Pure Java version (3577L/20K).
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 | // modified by Stefan Reich |
22 | |
23 | // Implements the Set interface more compactly than |
24 | // java.util.HashSet by using a closed hashtable. |
25 | |
26 | sclass CompactHashSet<A> extends java.util.AbstractSet<A> { |
27 | protected final static int INITIAL_SIZE = 0; |
28 | protected final static int FIRST_INCREMENT = 3; // 2 or 3 are useful |
29 | public final static double LOAD_FACTOR = 0.75; |
30 | |
31 | protected final static Object nullObject = new Object(); |
32 | protected final static Object deletedObject = new Object(); |
33 | protected int elements; |
34 | protected int freecells; |
35 | protected A[] objects; |
36 | ifndef NoModCount |
37 | protected int modCount; |
38 | endifndef |
39 | |
40 | CompactHashSet() { |
41 | this(INITIAL_SIZE); |
42 | } |
43 | |
44 | *(int size) { |
45 | objects = (A[]) (size == 0 ? emptyObjectArray() : new O[size]); |
46 | elements = 0; |
47 | freecells = objects.length; |
48 | ifndef NoModCount |
49 | modCount = 0; |
50 | endifndef |
51 | } |
52 | |
53 | *(Collection<A> c) { |
54 | this(c.size()); |
55 | addAll(c); |
56 | } |
57 | |
58 | @Override |
59 | public Iterator<A> iterator() { |
60 | return new CompactHashIterator<A>(); |
61 | } |
62 | |
63 | @Override |
64 | public int size() { |
65 | return elements; |
66 | } |
67 | |
68 | @Override |
69 | public boolean isEmpty() { |
70 | return elements == 0; |
71 | } |
72 | |
73 | @Override |
74 | public boolean contains(Object o) { |
75 | ret find(o) != null; |
76 | } |
77 | |
78 | synchronized A find(O o) { |
79 | if (objects.length == 0) null; |
80 | |
81 | if (o == null) o = nullObject; |
82 | |
83 | int hash = o.hashCode(); |
84 | int index = (hash & 0x7FFFFFFF) % objects.length; |
85 | int offset = 1; |
86 | |
87 | // search for the object (continue while !null and !this object) |
88 | while(objects[index] != null && |
89 | !(objects[index].hashCode() == hash && |
90 | objects[index].equals(o))) { |
91 | index = ((index + offset) & 0x7FFFFFFF) % objects.length; |
92 | offset = offset*2 + 1; |
93 | |
94 | if (offset == -1) |
95 | offset = 2; |
96 | } |
97 | |
98 | ret objects[index]; |
99 | } |
100 | |
101 | bool removeIfSame(O o) { |
102 | A value = find(o); |
103 | if (value == o) { |
104 | remove(value); |
105 | true; |
106 | } |
107 | false; |
108 | } |
109 | |
110 | @Override |
111 | synchronized public boolean add(Object o) { |
112 | if (objects.length == 0) rehash(FIRST_INCREMENT); |
113 | |
114 | if (o == null) o = nullObject; |
115 | |
116 | int hash = o.hashCode(); |
117 | int index = (hash & 0x7FFFFFFF) % objects.length; |
118 | int offset = 1; |
119 | int deletedix = -1; |
120 | |
121 | // search for the object (continue while !null and !this object) |
122 | while(objects[index] != null && |
123 | !(objects[index].hashCode() == hash && |
124 | objects[index].equals(o))) { |
125 | |
126 | // if there's a deleted object here we can put this object here, |
127 | // provided it's not in here somewhere else already |
128 | if (objects[index] == deletedObject) |
129 | deletedix = index; |
130 | |
131 | index = ((index + offset) & 0x7FFFFFFF) % objects.length; |
132 | offset = offset*2 + 1; |
133 | |
134 | if (offset == -1) |
135 | offset = 2; |
136 | } |
137 | |
138 | if (objects[index] == null) { // wasn't present already |
139 | if (deletedix != -1) // reusing a deleted cell |
140 | index = deletedix; |
141 | else |
142 | freecells--; |
143 | |
144 | ifndef NoModCount |
145 | modCount++; |
146 | endifndef |
147 | elements++; |
148 | |
149 | // here we face a problem regarding generics: |
150 | // add(A o) is not possible because of the null Object. We cant do 'new A()' or '(A) new Object()' |
151 | // so adding an empty object is a problem here |
152 | // If (! o instanceof A) : This will cause a class cast exception |
153 | // If (o instanceof A) : This will work fine |
154 | |
155 | objects[index] = (A) o; |
156 | |
157 | // do we need to rehash? |
158 | if (1 - (freecells / (double) objects.length) > LOAD_FACTOR) |
159 | rehash(); |
160 | true; |
161 | } else // was there already |
162 | return false; |
163 | } |
164 | |
165 | @Override |
166 | synchronized public boolean remove(Object o) { |
167 | if (objects.length == 0) false; |
168 | if (o == null) o = nullObject; |
169 | |
170 | int hash = o.hashCode(); |
171 | int index = (hash & 0x7FFFFFFF) % objects.length; |
172 | int offset = 1; |
173 | |
174 | // search for the object (continue while !null and !this object) |
175 | while(objects[index] != null && |
176 | !(objects[index].hashCode() == hash && |
177 | objects[index].equals(o))) { |
178 | index = ((index + offset) & 0x7FFFFFFF) % objects.length; |
179 | offset = offset*2 + 1; |
180 | |
181 | if (offset == -1) |
182 | offset = 2; |
183 | } |
184 | |
185 | // we found the right position, now do the removal |
186 | if (objects[index] != null) { |
187 | // we found the object |
188 | |
189 | // same problem here as with add |
190 | objects[index] = (A) deletedObject; |
191 | ifndef NoModCount |
192 | modCount++; |
193 | endifndef |
194 | elements--; |
195 | return true; |
196 | } else |
197 | // we did not find the object |
198 | return false; |
199 | } |
200 | |
201 | @Override |
202 | synchronized public void clear() { |
203 | elements = 0; |
204 | for (int ix = 0; ix < objects.length; ix++) |
205 | objects[ix] = null; |
206 | freecells = objects.length; |
207 | ifndef NoModCount |
208 | modCount++; |
209 | endifndef |
210 | } |
211 | |
212 | @Override |
213 | synchronized public Object[] toArray() { |
214 | Object[] result = new Object[elements]; |
215 | Object[] objects = this.objects; |
216 | int pos = 0; |
217 | for (int i = 0; i < objects.length; i++) |
218 | if (objects[i] != null && objects[i] != deletedObject) { |
219 | if (objects[i] == nullObject) |
220 | result[pos++] = null; |
221 | else |
222 | result[pos++] = objects[i]; |
223 | } |
224 | // unchecked because it should only contain A |
225 | return result; |
226 | } |
227 | |
228 | // not sure if this needs to have generics |
229 | @Override |
230 | synchronized public <T> T[] toArray(T[] a) { |
231 | int size = elements; |
232 | if (a.length < size) |
233 | a = (T[])java.lang.reflect.Array.newInstance( |
234 | a.getClass().getComponentType(), size); |
235 | A[] objects = this.objects; |
236 | int pos = 0; |
237 | for (int i = 0; i < objects.length; i++) |
238 | if (objects[i] != null && objects[i] != deletedObject) { |
239 | if (objects[i] == nullObject) |
240 | a[pos++] = null; |
241 | else |
242 | a[pos++] = (T) objects[i]; |
243 | } |
244 | return a; |
245 | } |
246 | |
247 | protected void rehash() { |
248 | int garbagecells = objects.length - (elements + freecells); |
249 | if (garbagecells / (double) objects.length > 0.05) |
250 | // rehash with same size |
251 | rehash(objects.length); |
252 | else |
253 | // rehash with increased capacity |
254 | rehash(objects.length*2 + 1); |
255 | } |
256 | |
257 | protected void rehash(int newCapacity) { |
258 | int oldCapacity = objects.length; |
259 | @SuppressWarnings("unchecked") |
260 | A[] newObjects = (A[]) new Object[newCapacity]; |
261 | |
262 | for (int ix = 0; ix < oldCapacity; ix++) { |
263 | Object o = objects[ix]; |
264 | if (o == null || o == deletedObject) |
265 | continue; |
266 | |
267 | int hash = o.hashCode(); |
268 | int index = (hash & 0x7FFFFFFF) % newCapacity; |
269 | int offset = 1; |
270 | |
271 | // search for the object |
272 | while(newObjects[index] != null) { // no need to test for duplicates |
273 | index = ((index + offset) & 0x7FFFFFFF) % newCapacity; |
274 | offset = offset*2 + 1; |
275 | |
276 | if (offset == -1) |
277 | offset = 2; |
278 | } |
279 | |
280 | newObjects[index] = (A) o; |
281 | } |
282 | |
283 | objects = newObjects; |
284 | freecells = objects.length - elements; |
285 | } |
286 | |
287 | private class CompactHashIterator<T> implements Iterator<T> { |
288 | private int index; |
289 | private int lastReturned = -1; |
290 | |
291 | ifndef NoModCount |
292 | private int expectedModCount; |
293 | endifndef |
294 | |
295 | @SuppressWarnings("empty-statement") |
296 | public CompactHashIterator() { |
297 | synchronized(CompactHashSet.this) { |
298 | for (index = 0; index < objects.length && |
299 | (objects[index] == null || |
300 | objects[index] == deletedObject); index++) |
301 | ; |
302 | ifndef NoModCount |
303 | expectedModCount = modCount; |
304 | endifndef |
305 | } |
306 | } |
307 | |
308 | @Override |
309 | public boolean hasNext() { |
310 | synchronized(CompactHashSet.this) { |
311 | return index < objects.length; |
312 | } |
313 | } |
314 | |
315 | @SuppressWarnings("empty-statement") |
316 | @Override |
317 | public T next() { |
318 | synchronized(CompactHashSet.this) { |
319 | /*if (modCount != expectedModCount) |
320 | throw new ConcurrentModificationException();*/ |
321 | int length = objects.length; |
322 | if (index >= length) { |
323 | lastReturned = -2; |
324 | throw new NoSuchElementException(); |
325 | } |
326 | |
327 | lastReturned = index; |
328 | for (index += 1; index < length && |
329 | (objects[index] == null || |
330 | objects[index] == deletedObject); index++) |
331 | ; |
332 | if (objects[lastReturned] == nullObject) |
333 | return null; |
334 | else |
335 | return (T) objects[lastReturned]; |
336 | } |
337 | } |
338 | |
339 | @Override |
340 | public void remove() { |
341 | synchronized(CompactHashSet.this) { |
342 | ifndef NoModCount |
343 | if (modCount != expectedModCount) |
344 | throw new ConcurrentModificationException(); |
345 | endifndef |
346 | if (lastReturned == -1 || lastReturned == -2) |
347 | throw new IllegalStateException(); |
348 | // delete object |
349 | if (objects[lastReturned] != null && objects[lastReturned] != deletedObject) { |
350 | objects[lastReturned] = (A) deletedObject; |
351 | elements--; |
352 | ifndef NoModCount |
353 | modCount++; |
354 | expectedModCount = modCount; // this is expected; we made the change |
355 | endifndef |
356 | } |
357 | } |
358 | } |
359 | } |
360 | |
361 | synchronized int capacity() { ret objects.length; } |
362 | |
363 | // returns true if there was a shrink |
364 | synchronized bool shrinkToFactor(double factor) { |
365 | if (factor > LOAD_FACTOR) |
366 | fail("Shrink factor must be equal to or smaller than load factor: " + factor + " / " + LOAD_FACTOR); |
367 | int newCapacity = max(INITIAL_SIZE, iround(size()/factor); |
368 | if (newCapacity >= capacity()) false; |
369 | rehash(newCapacity); |
370 | true; |
371 | } |
372 | } |
from https://github.com/ontopia/ontopia/blob/HEAD/ontopia-engine/src/main/java/net/ontopia/utils/CompactHashSet.java
download show line numbers debug dex old transpilations
Travelled to 15 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt, xrpafgyirdlv
No comments. add comment
Snippet ID: | #1012355 |
Snippet name: | CompactHashSet - synchronized |
Eternal ID of this version: | #1012355/19 |
Text MD5: | c5502feea19a5b462cb9bb9974c892eb |
Transpilation MD5: | 526cdbe5500028e4d02a4d9a6a3825ed |
Author: | stefan |
Category: | javax / collections |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2021-08-11 01:23:59 |
Source code size: | 10536 bytes / 372 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 624 / 1329 |
Version history: | 18 change(s) |
Referenced in: | [show references] |