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 | // & allows custom hasher. |
26 | |
27 | sclass CustomCompactHashSet<A> extends AbstractSet<A> { |
28 | protected final static int INITIAL_SIZE = 3; |
29 | protected 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 | protected int modCount; |
37 | Hasher<A> hasher; // optional |
38 | |
39 | *() { |
40 | this(INITIAL_SIZE); |
41 | } |
42 | |
43 | *(int size) { |
44 | // NOTE: If array size is 0, we get a |
45 | // "java.lang.ArithmeticException: / by zero" in add(Object). |
46 | objects = (A[]) new Object[(size==0 ? 1 : size)]; |
47 | elements = 0; |
48 | freecells = objects.length; |
49 | modCount = 0; |
50 | } |
51 | |
52 | *(Collection<A> c) { |
53 | this(c.size()); |
54 | addAll(c); |
55 | } |
56 | |
57 | *(Hasher<A> hasher) { |
58 | this(); |
59 | this.hasher = hasher; |
60 | } |
61 | |
62 | @Override |
63 | public Iterator<A> iterator() { |
64 | return new CompactHashIterator<A>(); |
65 | } |
66 | |
67 | @Override |
68 | public int size() { |
69 | return elements; |
70 | } |
71 | |
72 | @Override |
73 | public boolean isEmpty() { |
74 | return elements == 0; |
75 | } |
76 | |
77 | @Override |
78 | public boolean contains(Object o) { |
79 | ret find(o) != null; |
80 | } |
81 | |
82 | synchronized A find(O o) { |
83 | if (o == null) o = nullObject; |
84 | |
85 | int hash = elementHashCode(o); |
86 | int index = (hash & 0x7FFFFFFF) % objects.length; |
87 | int offset = 1; |
88 | |
89 | // search for the object (continue while !null and !this object) |
90 | while(objects[index] != null && |
91 | !(elementHashCode(objects[index]) == hash && |
92 | elementEquals(objects[index], o))) { |
93 | index = ((index + offset) & 0x7FFFFFFF) % objects.length; |
94 | offset = offset*2 + 1; |
95 | |
96 | if (offset == -1) |
97 | offset = 2; |
98 | } |
99 | |
100 | ret objects[index]; |
101 | } |
102 | |
103 | bool removeIfSame(O o) { |
104 | A value = find(o); |
105 | if (value == o) { |
106 | remove(value); |
107 | true; |
108 | } |
109 | false; |
110 | } |
111 | |
112 | @Override |
113 | synchronized public boolean add(Object o) { |
114 | if (o == null) o = nullObject; |
115 | |
116 | int hash = elementHashCode(o); |
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 | !(elementHashCode(objects[index]) == hash && |
124 | elementEquals(objects[index], 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 | modCount++; |
145 | elements++; |
146 | |
147 | // here we face a problem regarding generics: |
148 | // add(A o) is not possible because of the null Object. We cant do 'new A()' or '(A) new Object()' |
149 | // so adding an empty object is a problem here |
150 | // If (! o instanceof A) : This will cause a class cast exception |
151 | // If (o instanceof A) : This will work fine |
152 | |
153 | objects[index] = (A) o; |
154 | |
155 | // do we need to rehash? |
156 | if (1 - (freecells / (double) objects.length) > LOAD_FACTOR) |
157 | rehash(); |
158 | return true; |
159 | } else // was there already |
160 | return false; |
161 | } |
162 | |
163 | @Override |
164 | synchronized public boolean remove(Object o) { |
165 | if (o == null) o = nullObject; |
166 | |
167 | int hash = elementHashCode(o); |
168 | int index = (hash & 0x7FFFFFFF) % objects.length; |
169 | int offset = 1; |
170 | |
171 | // search for the object (continue while !null and !this object) |
172 | while(objects[index] != null && |
173 | !(elementHashCode(objects[index]) == hash && |
174 | elementEquals(objects[index], o))) { |
175 | index = ((index + offset) & 0x7FFFFFFF) % objects.length; |
176 | offset = offset*2 + 1; |
177 | |
178 | if (offset == -1) |
179 | offset = 2; |
180 | } |
181 | |
182 | // we found the right position, now do the removal |
183 | if (objects[index] != null) { |
184 | // we found the object |
185 | |
186 | // same problem here as with add |
187 | objects[index] = (A) deletedObject; |
188 | modCount++; |
189 | elements--; |
190 | return true; |
191 | } else |
192 | // we did not find the object |
193 | return false; |
194 | } |
195 | |
196 | @Override |
197 | synchronized public void clear() { |
198 | elements = 0; |
199 | for (int ix = 0; ix < objects.length; ix++) |
200 | objects[ix] = null; |
201 | freecells = objects.length; |
202 | modCount++; |
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 | private int expectedModCount; |
285 | |
286 | @SuppressWarnings("empty-statement") |
287 | public CompactHashIterator() { |
288 | synchronized(CustomCompactHashSet.this) { |
289 | for (index = 0; index < objects.length && |
290 | (objects[index] == null || |
291 | objects[index] == deletedObject); index++) |
292 | ; |
293 | expectedModCount = modCount; |
294 | } |
295 | } |
296 | |
297 | @Override |
298 | public boolean hasNext() { |
299 | synchronized(CustomCompactHashSet.this) { |
300 | return index < objects.length; |
301 | } |
302 | } |
303 | |
304 | @SuppressWarnings("empty-statement") |
305 | @Override |
306 | public T next() { |
307 | synchronized(CustomCompactHashSet.this) { |
308 | /*if (modCount != expectedModCount) |
309 | throw new ConcurrentModificationException();*/ |
310 | int length = objects.length; |
311 | if (index >= length) { |
312 | lastReturned = -2; |
313 | throw new NoSuchElementException(); |
314 | } |
315 | |
316 | lastReturned = index; |
317 | for (index += 1; index < length && |
318 | (objects[index] == null || |
319 | objects[index] == deletedObject); index++) |
320 | ; |
321 | if (objects[lastReturned] == nullObject) |
322 | return null; |
323 | else |
324 | return (T) objects[lastReturned]; |
325 | } |
326 | } |
327 | |
328 | @Override |
329 | public void remove() { |
330 | synchronized(CustomCompactHashSet.this) { |
331 | if (modCount != expectedModCount) |
332 | throw new ConcurrentModificationException(); |
333 | if (lastReturned == -1 || lastReturned == -2) |
334 | throw new IllegalStateException(); |
335 | // delete object |
336 | if (objects[lastReturned] != null && objects[lastReturned] != deletedObject) { |
337 | objects[lastReturned] = (A) deletedObject; |
338 | elements--; |
339 | modCount++; |
340 | expectedModCount = modCount; // this is expected; we made the change |
341 | } |
342 | } |
343 | } |
344 | } |
345 | |
346 | int elementHashCode(O o) { |
347 | if (hasher == null || o == nullObject || o == deletedObject) ret _hashCode(o); |
348 | ret hasher.hashCode((A) o); |
349 | } |
350 | |
351 | bool elementEquals(O a, O b) { |
352 | if (hasher == null) ret eq(a, b); |
353 | if (a == nullObject || a == deletedObject |
354 | || b == nullObject || b == deletedObject) ret a == b; |
355 | ret hasher.equals((A) a, (A) b); |
356 | } |
357 | } |
Began life as a copy of #1012355
download show line numbers debug dex old transpilations
Travelled to 13 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #1012816 |
Snippet name: | CustomCompactHashSet - CompactHashSet with custom Hasher |
Eternal ID of this version: | #1012816/11 |
Text MD5: | 03142d3cb604b963f5ca3e688522583f |
Author: | stefan |
Category: | javax / collections |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2017-12-24 20:44:17 |
Source code size: | 10177 bytes / 357 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 588 / 1119 |
Version history: | 10 change(s) |
Referenced in: | [show references] |