Libraryless. Click here for Pure Java version (2921L/19K).
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 | final sclass CompactIdentityHashSet<A> extends AbstractSet<A> { |
24 | protected final static int INITIAL_SIZE = 3; |
25 | protected final static double LOAD_FACTOR = 0.75; |
26 | |
27 | protected final static Object nullObject = new Object(); |
28 | protected final static Object deletedObject = new Object(); |
29 | protected int elements; |
30 | protected int freecells; |
31 | protected A[] objects; |
32 | ifndef NoModCount |
33 | protected int modCount; |
34 | endifndef |
35 | |
36 | CompactIdentityHashSet() { |
37 | this(INITIAL_SIZE); |
38 | } |
39 | |
40 | *(int size) { |
41 | // NOTE: If array size is 0, we get a |
42 | // "java.lang.ArithmeticException: / by zero" in add(Object). |
43 | objects = (A[]) new Object[(size==0 ? 1 : size)]; |
44 | elements = 0; |
45 | freecells = objects.length; |
46 | ifndef NoModCount |
47 | modCount = 0; |
48 | endifndef |
49 | } |
50 | |
51 | *(Collection<A> c) { |
52 | this(c.size()); |
53 | addAll(c); |
54 | } |
55 | |
56 | @Override |
57 | public Iterator<A> iterator() { |
58 | return new CompactHashIterator<A>(); |
59 | } |
60 | |
61 | @Override |
62 | public int size() { |
63 | return elements; |
64 | } |
65 | |
66 | @Override |
67 | public boolean isEmpty() { |
68 | return elements == 0; |
69 | } |
70 | |
71 | @Override |
72 | public boolean contains(Object o) { |
73 | ret find(o) != null; |
74 | } |
75 | |
76 | synchronized A find(O o) { |
77 | if (o == null) o = nullObject; |
78 | |
79 | int hash = elementHashCode(o); |
80 | int index = (hash & 0x7FFFFFFF) % objects.length; |
81 | int offset = 1; |
82 | |
83 | // search for the object (continue while !null and !this object) |
84 | while(objects[index] != null && |
85 | !(elementHashCode(objects[index]) == hash && |
86 | elementEquals(objects[index], o))) { |
87 | index = ((index + offset) & 0x7FFFFFFF) % objects.length; |
88 | offset = offset*2 + 1; |
89 | |
90 | if (offset == -1) |
91 | offset = 2; |
92 | } |
93 | |
94 | ret objects[index]; |
95 | } |
96 | |
97 | bool removeIfSame(O o) { |
98 | A value = find(o); |
99 | if (value == o) { |
100 | remove(value); |
101 | true; |
102 | } |
103 | false; |
104 | } |
105 | |
106 | @Override |
107 | synchronized public boolean add(Object o) { |
108 | if (o == null) o = nullObject; |
109 | |
110 | int hash = elementHashCode(o); |
111 | int index = (hash & 0x7FFFFFFF) % objects.length; |
112 | int offset = 1; |
113 | int deletedix = -1; |
114 | |
115 | // search for the object (continue while !null and !this object) |
116 | while(objects[index] != null && |
117 | !(elementHashCode(objects[index]) == hash && |
118 | elementEquals(objects[index], o))) { |
119 | |
120 | // if there's a deleted object here we can put this object here, |
121 | // provided it's not in here somewhere else already |
122 | if (objects[index] == deletedObject) |
123 | deletedix = index; |
124 | |
125 | index = ((index + offset) & 0x7FFFFFFF) % objects.length; |
126 | offset = offset*2 + 1; |
127 | |
128 | if (offset == -1) |
129 | offset = 2; |
130 | } |
131 | |
132 | if (objects[index] == null) { // wasn't present already |
133 | if (deletedix != -1) // reusing a deleted cell |
134 | index = deletedix; |
135 | else |
136 | freecells--; |
137 | |
138 | ifndef NoModCount |
139 | modCount++; |
140 | endifndef |
141 | elements++; |
142 | |
143 | // here we face a problem regarding generics: |
144 | // add(A o) is not possible because of the null Object. We cant do 'new A()' or '(A) new Object()' |
145 | // so adding an empty object is a problem here |
146 | // If (! o instanceof A) : This will cause a class cast exception |
147 | // If (o instanceof A) : This will work fine |
148 | |
149 | objects[index] = (A) o; |
150 | |
151 | // do we need to rehash? |
152 | if (1 - (freecells / (double) objects.length) > LOAD_FACTOR) |
153 | rehash(); |
154 | return true; |
155 | } else // was there already |
156 | return false; |
157 | } |
158 | |
159 | @Override |
160 | synchronized public boolean remove(Object o) { |
161 | if (o == null) o = nullObject; |
162 | |
163 | int hash = elementHashCode(o); |
164 | int index = (hash & 0x7FFFFFFF) % objects.length; |
165 | int offset = 1; |
166 | |
167 | // search for the object (continue while !null and !this object) |
168 | while(objects[index] != null && |
169 | !(elementHashCode(objects[index]) == hash && |
170 | elementEquals(objects[index], o))) { |
171 | index = ((index + offset) & 0x7FFFFFFF) % objects.length; |
172 | offset = offset*2 + 1; |
173 | |
174 | if (offset == -1) |
175 | offset = 2; |
176 | } |
177 | |
178 | // we found the right position, now do the removal |
179 | if (objects[index] != null) { |
180 | // we found the object |
181 | |
182 | // same problem here as with add |
183 | objects[index] = (A) deletedObject; |
184 | ifndef NoModCount |
185 | modCount++; |
186 | endifndef |
187 | elements--; |
188 | return true; |
189 | } else |
190 | // we did not find the object |
191 | return false; |
192 | } |
193 | |
194 | @Override |
195 | synchronized public void clear() { |
196 | elements = 0; |
197 | for (int ix = 0; ix < objects.length; ix++) |
198 | objects[ix] = null; |
199 | freecells = objects.length; |
200 | ifndef NoModCount |
201 | modCount++; |
202 | endifndef |
203 | } |
204 | |
205 | @Override |
206 | synchronized public Object[] toArray() { |
207 | Object[] result = new Object[elements]; |
208 | Object[] objects = this.objects; |
209 | int pos = 0; |
210 | for (int i = 0; i < objects.length; i++) |
211 | if (objects[i] != null && objects[i] != deletedObject) { |
212 | if (objects[i] == nullObject) |
213 | result[pos++] = null; |
214 | else |
215 | result[pos++] = objects[i]; |
216 | } |
217 | // unchecked because it should only contain A |
218 | return result; |
219 | } |
220 | |
221 | // not sure if this needs to have generics |
222 | @Override |
223 | synchronized public <T> T[] toArray(T[] a) { |
224 | int size = elements; |
225 | if (a.length < size) |
226 | a = (T[])java.lang.reflect.Array.newInstance( |
227 | a.getClass().getComponentType(), size); |
228 | A[] objects = this.objects; |
229 | int pos = 0; |
230 | for (int i = 0; i < objects.length; i++) |
231 | if (objects[i] != null && objects[i] != deletedObject) { |
232 | if (objects[i] == nullObject) |
233 | a[pos++] = null; |
234 | else |
235 | a[pos++] = (T) objects[i]; |
236 | } |
237 | return a; |
238 | } |
239 | |
240 | protected void rehash() { |
241 | int gargagecells = objects.length - (elements + freecells); |
242 | if (gargagecells / (double) objects.length > 0.05) |
243 | // rehash with same size |
244 | rehash(objects.length); |
245 | else |
246 | // rehash with increased capacity |
247 | rehash(objects.length*2 + 1); |
248 | } |
249 | |
250 | protected void rehash(int newCapacity) { |
251 | int oldCapacity = objects.length; |
252 | @SuppressWarnings("unchecked") |
253 | A[] newObjects = (A[]) new Object[newCapacity]; |
254 | |
255 | for (int ix = 0; ix < oldCapacity; ix++) { |
256 | Object o = objects[ix]; |
257 | if (o == null || o == deletedObject) |
258 | continue; |
259 | |
260 | int hash = elementHashCode(o); |
261 | int index = (hash & 0x7FFFFFFF) % newCapacity; |
262 | int offset = 1; |
263 | |
264 | // search for the object |
265 | while(newObjects[index] != null) { // no need to test for duplicates |
266 | index = ((index + offset) & 0x7FFFFFFF) % newCapacity; |
267 | offset = offset*2 + 1; |
268 | |
269 | if (offset == -1) |
270 | offset = 2; |
271 | } |
272 | |
273 | newObjects[index] = (A) o; |
274 | } |
275 | |
276 | objects = newObjects; |
277 | freecells = objects.length - elements; |
278 | } |
279 | |
280 | private class CompactHashIterator<T> implements Iterator<T> { |
281 | private int index; |
282 | private int lastReturned = -1; |
283 | |
284 | ifndef NoModCount |
285 | private int expectedModCount; |
286 | endifndef |
287 | |
288 | @SuppressWarnings("empty-statement") |
289 | public CompactHashIterator() { |
290 | synchronized(CompactIdentityHashSet.this) { |
291 | for (index = 0; index < objects.length && |
292 | (objects[index] == null || |
293 | objects[index] == deletedObject); index++) |
294 | ; |
295 | ifndef NoModCount |
296 | expectedModCount = modCount; |
297 | endifndef |
298 | } |
299 | } |
300 | |
301 | @Override |
302 | public boolean hasNext() { |
303 | synchronized(CompactIdentityHashSet.this) { |
304 | return index < objects.length; |
305 | } |
306 | } |
307 | |
308 | @SuppressWarnings("empty-statement") |
309 | @Override |
310 | public T next() { |
311 | synchronized(CompactIdentityHashSet.this) { |
312 | /*if (modCount != expectedModCount) |
313 | throw new ConcurrentModificationException();*/ |
314 | int length = objects.length; |
315 | if (index >= length) { |
316 | lastReturned = -2; |
317 | throw new NoSuchElementException(); |
318 | } |
319 | |
320 | lastReturned = index; |
321 | for (index += 1; index < length && |
322 | (objects[index] == null || |
323 | objects[index] == deletedObject); index++) |
324 | ; |
325 | if (objects[lastReturned] == nullObject) |
326 | return null; |
327 | else |
328 | return (T) objects[lastReturned]; |
329 | } |
330 | } |
331 | |
332 | @Override |
333 | public void remove() { |
334 | synchronized(CompactIdentityHashSet.this) { |
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 | |
354 | int elementHashCode(O o) { |
355 | ret identityHashCode(o); |
356 | } |
357 | |
358 | bool elementEquals(O a, O b) { |
359 | ret a == b; |
360 | } |
361 | } |
Began life as a copy of #1012816
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: | #1028518 |
Snippet name: | CompactIdentityHashSet - CompactHashSet using object identity hashCode/equals |
Eternal ID of this version: | #1028518/5 |
Text MD5: | dee8d12db16126219ec8c9bbed01ea0c |
Transpilation MD5: | a220a4b3cff99631bd1a89fcbd118a06 |
Author: | stefan |
Category: | javax / collections |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2020-06-24 00:09:29 |
Source code size: | 10115 bytes / 361 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 284 / 597 |
Version history: | 4 change(s) |
Referenced in: | [show references] |