Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

357
LINES

< > BotCompany Repo | #1012816 // CustomCompactHashSet - CompactHashSet with custom Hasher

JavaX fragment (include)

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  
}

Author comment

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]