Libraryless. Click here for Pure Java version (3422L/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 | // Note: equals is always called on the _stored_ object, not the one |
27 | // passed as an argument to find(), contains() etc. |
28 | // (In case you want to put special magic in your equals() function) |
29 | |
30 | sclass UnsynchronizedCompactHashSet<A> extends java.util.AbstractSet<A> { |
31 | protected final static int INITIAL_SIZE = 3; |
32 | public final static double LOAD_FACTOR = 0.75; |
33 | |
34 | protected final static Object nullObject = new Object(); |
35 | protected final static Object deletedObject = new Object(); |
36 | protected int elements; |
37 | protected int freecells; |
38 | protected A[] objects; |
39 | ifndef NoModCount |
40 | protected int modCount; |
41 | endifndef |
42 | |
43 | UnsynchronizedCompactHashSet() { |
44 | this(INITIAL_SIZE); |
45 | } |
46 | |
47 | *(int size) { |
48 | // NOTE: If array size is 0, we get a |
49 | // "java.lang.ArithmeticException: / by zero" in add(Object). |
50 | objects = (A[]) new Object[(size==0 ? 1 : size)]; |
51 | elements = 0; |
52 | freecells = objects.length; |
53 | ifndef NoModCount |
54 | modCount = 0; |
55 | endifndef |
56 | } |
57 | |
58 | *(Collection<A> c) { |
59 | this(c.size()); |
60 | addAll(c); |
61 | } |
62 | |
63 | @Override |
64 | public Iterator<A> iterator() { |
65 | return new CompactHashIterator<A>(); |
66 | } |
67 | |
68 | @Override |
69 | public int size() { |
70 | return elements; |
71 | } |
72 | |
73 | @Override |
74 | public boolean isEmpty() { |
75 | return elements == 0; |
76 | } |
77 | |
78 | @Override |
79 | public boolean contains(Object o) { |
80 | ret find(o) != null; |
81 | } |
82 | |
83 | A find(O o) { |
84 | if (o == null) o = nullObject; |
85 | |
86 | int hash = o.hashCode(); |
87 | int index = (hash & 0x7FFFFFFF) % objects.length; |
88 | int offset = 1; |
89 | |
90 | // search for the object (continue while !null and !this object) |
91 | while(objects[index] != null && |
92 | !(objects[index].hashCode() == hash && |
93 | objects[index].equals(o))) { |
94 | index = ((index + offset) & 0x7FFFFFFF) % objects.length; |
95 | offset = offset*2 + 1; |
96 | |
97 | if (offset == -1) |
98 | offset = 2; |
99 | } |
100 | |
101 | ret objects[index]; |
102 | } |
103 | |
104 | bool removeIfSame(O o) { |
105 | A value = find(o); |
106 | if (value == o) { |
107 | remove(value); |
108 | true; |
109 | } |
110 | false; |
111 | } |
112 | |
113 | @Override |
114 | public boolean add(Object o) { |
115 | if (o == null) o = nullObject; |
116 | |
117 | int hash = o.hashCode(); |
118 | int index = (hash & 0x7FFFFFFF) % objects.length; |
119 | int offset = 1; |
120 | int deletedix = -1; |
121 | |
122 | // search for the object (continue while !null and !this object) |
123 | while(objects[index] != null && |
124 | !(objects[index].hashCode() == hash && |
125 | objects[index].equals(o))) { |
126 | |
127 | // if there's a deleted object here we can put this object here, |
128 | // provided it's not in here somewhere else already |
129 | if (objects[index] == deletedObject) |
130 | deletedix = index; |
131 | |
132 | index = ((index + offset) & 0x7FFFFFFF) % objects.length; |
133 | offset = offset*2 + 1; |
134 | |
135 | if (offset == -1) |
136 | offset = 2; |
137 | } |
138 | |
139 | if (objects[index] == null) { // wasn't present already |
140 | if (deletedix != -1) // reusing a deleted cell |
141 | index = deletedix; |
142 | else |
143 | freecells--; |
144 | |
145 | ifndef NoModCount |
146 | modCount++; |
147 | endifndef |
148 | elements++; |
149 | |
150 | // here we face a problem regarding generics: |
151 | // add(A o) is not possible because of the null Object. We cant do 'new A()' or '(A) new Object()' |
152 | // so adding an empty object is a problem here |
153 | // If (! o instanceof A) : This will cause a class cast exception |
154 | // If (o instanceof A) : This will work fine |
155 | |
156 | objects[index] = (A) o; |
157 | |
158 | // do we need to rehash? |
159 | if (1 - (freecells / (double) objects.length) > LOAD_FACTOR) |
160 | rehash(); |
161 | return true; |
162 | } else // was there already |
163 | return false; |
164 | } |
165 | |
166 | @Override |
167 | public boolean remove(Object o) { |
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 | 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 | 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 | 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 | for (index = 0; index < objects.length && |
298 | (objects[index] == null || |
299 | objects[index] == deletedObject); index++) |
300 | ; |
301 | ifndef NoModCount |
302 | expectedModCount = modCount; |
303 | endifndef |
304 | } |
305 | |
306 | @Override |
307 | public boolean hasNext() { |
308 | return index < objects.length; |
309 | } |
310 | |
311 | @SuppressWarnings("empty-statement") |
312 | @Override |
313 | public T next() { |
314 | /*if (modCount != expectedModCount) |
315 | throw new ConcurrentModificationException();*/ |
316 | int length = objects.length; |
317 | if (index >= length) { |
318 | lastReturned = -2; |
319 | throw new NoSuchElementException(); |
320 | } |
321 | |
322 | lastReturned = index; |
323 | for (index += 1; index < length && |
324 | (objects[index] == null || |
325 | objects[index] == deletedObject); index++) |
326 | ; |
327 | if (objects[lastReturned] == nullObject) |
328 | return null; |
329 | else |
330 | return (T) objects[lastReturned]; |
331 | } |
332 | |
333 | @Override |
334 | public void remove() { |
335 | ifndef NoModCount |
336 | if (modCount != expectedModCount) |
337 | throw new ConcurrentModificationException(); |
338 | endifndef |
339 | if (lastReturned == -1 || lastReturned == -2) |
340 | throw new IllegalStateException(); |
341 | // delete object |
342 | if (objects[lastReturned] != null && objects[lastReturned] != deletedObject) { |
343 | objects[lastReturned] = (A) deletedObject; |
344 | elements--; |
345 | ifndef NoModCount |
346 | modCount++; |
347 | expectedModCount = modCount; // this is expected; we made the change |
348 | endifndef |
349 | } |
350 | } |
351 | } |
352 | |
353 | int capacity() { ret objects.length; } |
354 | |
355 | // returns true if there was a shrink |
356 | bool shrinkToFactor(double factor) { |
357 | if (factor > LOAD_FACTOR) |
358 | fail("Shrink factor must be equal to or smaller than load factor: " + factor + " / " + LOAD_FACTOR); |
359 | int newCapacity = max(INITIAL_SIZE, iround(size()/factor); |
360 | if (newCapacity >= capacity()) false; |
361 | rehash(newCapacity); |
362 | true; |
363 | } |
364 | } |
Began life as a copy of #1012355
download show line numbers debug dex old transpilations
Travelled to 12 computer(s): bhatertpkbcr, ekrmjmnbrukm, elmgxqgtpvxh, mowyntqkapby, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt, wnsclhtenguj, xrpafgyirdlv
No comments. add comment
Snippet ID: | #1028522 |
Snippet name: | UnsynchronizedCompactHashSet |
Eternal ID of this version: | #1028522/3 |
Text MD5: | 2b57ce7d3600efb650167f10cb1f0e3b |
Transpilation MD5: | ec00f26395e432c3786334092a70bf8a |
Author: | stefan |
Category: | javax / collections |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2021-08-03 22:17:18 |
Source code size: | 10337 bytes / 364 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 251 / 2762 |
Version history: | 2 change(s) |
Referenced in: | [show references] |