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

361
LINES

< > BotCompany Repo | #1028518 // CompactIdentityHashSet - CompactHashSet using object identity hashCode/equals

JavaX fragment (include) [tags: use-pretranspiled]

Libraryless. Click here for Pure Java version (2921L/19K).

/*
 * #!
 * Ontopia Engine
 * #-
 * Copyright (C) 2001 - 2013 The Ontopia Project
 * #-
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * !#
 */

// modified by Stefan Reich

final sclass CompactIdentityHashSet<A> extends AbstractSet<A> {
  protected final static int INITIAL_SIZE = 3;
  protected final static double LOAD_FACTOR = 0.75;

  protected final static Object nullObject = new Object();
  protected final static Object deletedObject = new Object();
  protected int elements;
  protected int freecells;
  protected A[] objects;
  ifndef NoModCount
  protected int modCount;
  endifndef
  
  CompactIdentityHashSet() {
    this(INITIAL_SIZE);
  }

  *(int size) {
    // NOTE: If array size is 0, we get a
    // "java.lang.ArithmeticException: / by zero" in add(Object).
    objects = (A[]) new Object[(size==0 ? 1 : size)];
    elements = 0;
    freecells = objects.length;
    ifndef NoModCount
      modCount = 0;
    endifndef
  }

  *(Collection<A> c) {
    this(c.size());
    addAll(c);
  }
  
  @Override
  public Iterator<A> iterator() {
    return new CompactHashIterator<A>();
  }

  @Override
  public int size() {
    return elements;
  }

  @Override
  public boolean isEmpty() {
    return elements == 0;
  }

  @Override
  public boolean contains(Object o) {
    ret find(o) != null;
  }
  
  synchronized A find(O o) {
    if (o == null) o = nullObject;
    
    int hash = elementHashCode(o);
    int index = (hash & 0x7FFFFFFF) % objects.length;
    int offset = 1;

    // search for the object (continue while !null and !this object)
    while(objects[index] != null &&
        !(elementHashCode(objects[index]) == hash &&
          elementEquals(objects[index], o))) {
      index = ((index + offset) & 0x7FFFFFFF) % objects.length;
      offset = offset*2 + 1;

      if (offset == -1)
        offset = 2;
    }

    ret objects[index];
  }
  
  bool removeIfSame(O o) {
    A value = find(o);
    if (value == o) {
      remove(value);
      true;
    }
    false;
  }

  @Override
  synchronized public boolean add(Object o) {
    if (o == null) o = nullObject;

    int hash = elementHashCode(o);
    int index = (hash & 0x7FFFFFFF) % objects.length;
    int offset = 1;
    int deletedix = -1;
    
    // search for the object (continue while !null and !this object)
    while(objects[index] != null &&
          !(elementHashCode(objects[index]) == hash &&
            elementEquals(objects[index], o))) {

      // if there's a deleted object here we can put this object here,
      // provided it's not in here somewhere else already
      if (objects[index] == deletedObject)
        deletedix = index;
      
      index = ((index + offset) & 0x7FFFFFFF) % objects.length;
      offset = offset*2 + 1;

      if (offset == -1)
        offset = 2;
    }
    
    if (objects[index] == null) { // wasn't present already
      if (deletedix != -1) // reusing a deleted cell
        index = deletedix;
      else
        freecells--;

      ifndef NoModCount
        modCount++;
      endifndef
      elements++;

      // here we face a problem regarding generics:
      // add(A o) is not possible because of the null Object. We cant do 'new A()' or '(A) new Object()'
      // so adding an empty object is a problem here
      // If (! o instanceof A) : This will cause a class cast exception
      // If (o instanceof A) : This will work fine

      objects[index] = (A) o;
      
      // do we need to rehash?
      if (1 - (freecells / (double) objects.length) > LOAD_FACTOR)
        rehash();
      return true;
    } else // was there already 
      return false;
  }
  
  @Override
  synchronized public boolean remove(Object o) {
    if (o == null) o = nullObject;
    
    int hash = elementHashCode(o);
    int index = (hash & 0x7FFFFFFF) % objects.length;
    int offset = 1;
    
    // search for the object (continue while !null and !this object)
    while(objects[index] != null &&
          !(elementHashCode(objects[index]) == hash &&
            elementEquals(objects[index], o))) {
      index = ((index + offset) & 0x7FFFFFFF) % objects.length;
      offset = offset*2 + 1;

      if (offset == -1)
        offset = 2;
    }

    // we found the right position, now do the removal
    if (objects[index] != null) {
      // we found the object

      // same problem here as with add
      objects[index] = (A) deletedObject;
      ifndef NoModCount
        modCount++;
      endifndef
      elements--;
      return true;
    } else
      // we did not find the object
      return false;
  }
  
  @Override
  synchronized public void clear() {
    elements = 0;
    for (int ix = 0; ix < objects.length; ix++)
      objects[ix] = null;
    freecells = objects.length;
    ifndef NoModCount
      modCount++;
    endifndef
  }

  @Override
  synchronized public Object[] toArray() {
    Object[] result = new Object[elements];
    Object[] objects = this.objects;
    int pos = 0;
    for (int i = 0; i < objects.length; i++)
      if (objects[i] != null && objects[i] != deletedObject) {
        if (objects[i] == nullObject)
          result[pos++] = null;
        else
          result[pos++] = objects[i];
      }
    // unchecked because it should only contain A
    return result;
  }

  // not sure if this needs to have generics
  @Override
  synchronized public <T> T[] toArray(T[] a) {
    int size = elements;
    if (a.length < size)
      a = (T[])java.lang.reflect.Array.newInstance(
                                 a.getClass().getComponentType(), size);
    A[] objects = this.objects;
    int pos = 0;
    for (int i = 0; i < objects.length; i++)
      if (objects[i] != null && objects[i] != deletedObject) {
        if (objects[i] == nullObject)
          a[pos++] = null;
        else
          a[pos++] = (T) objects[i];
      }
    return a;
  }
  
  protected void rehash() {
    int gargagecells = objects.length - (elements + freecells);
    if (gargagecells / (double) objects.length > 0.05)
      // rehash with same size
      rehash(objects.length);
    else
      // rehash with increased capacity
      rehash(objects.length*2 + 1);
  }
  
  protected void rehash(int newCapacity) {
    int oldCapacity = objects.length;
    @SuppressWarnings("unchecked")
    A[] newObjects = (A[]) new Object[newCapacity];

    for (int ix = 0; ix < oldCapacity; ix++) {
      Object o = objects[ix];
      if (o == null || o == deletedObject)
        continue;
      
      int hash = elementHashCode(o);
      int index = (hash & 0x7FFFFFFF) % newCapacity;
      int offset = 1;

      // search for the object
      while(newObjects[index] != null) { // no need to test for duplicates
        index = ((index + offset) & 0x7FFFFFFF) % newCapacity;
        offset = offset*2 + 1;

        if (offset == -1)
          offset = 2;
      }

      newObjects[index] = (A) o;
    }

    objects = newObjects;
    freecells = objects.length - elements;
  }
  
  private class CompactHashIterator<T> implements Iterator<T> {
    private int index;
    private int lastReturned = -1;

    ifndef NoModCount
      private int expectedModCount;
    endifndef

    @SuppressWarnings("empty-statement")
    public CompactHashIterator() {
      synchronized(CompactIdentityHashSet.this) {
        for (index = 0; index < objects.length &&
                        (objects[index] == null ||
                        objects[index] == deletedObject); index++)
          ;
        ifndef NoModCount
          expectedModCount = modCount;
        endifndef
      }
    }

    @Override
    public boolean hasNext() {
      synchronized(CompactIdentityHashSet.this) {
        return index < objects.length;
      }
    }

    @SuppressWarnings("empty-statement")
    @Override
    public T next() {
      synchronized(CompactIdentityHashSet.this) {
        /*if (modCount != expectedModCount)
          throw new ConcurrentModificationException();*/
        int length = objects.length;
        if (index >= length) {
          lastReturned = -2;
          throw new NoSuchElementException();
        }
  
        lastReturned = index;
        for (index += 1; index < length &&
                         (objects[index] == null ||
                          objects[index] == deletedObject); index++)
          ;
        if (objects[lastReturned] == nullObject)
          return null;
        else
          return (T) objects[lastReturned];
      }
    }

    @Override
    public void remove() {
      synchronized(CompactIdentityHashSet.this) {
        ifndef NoModCount
          if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        endifndef
        if (lastReturned == -1 || lastReturned == -2)
          throw new IllegalStateException();
        // delete object
        if (objects[lastReturned] != null && objects[lastReturned] != deletedObject) {
          objects[lastReturned] = (A) deletedObject;
          elements--;
          ifndef NoModCount
            modCount++;
            expectedModCount = modCount; // this is expected; we made the change
          endifndef
        }
      }
    }
  }
  
  int elementHashCode(O o) {
    ret identityHashCode(o);
  }
  
  bool elementEquals(O a, O b) {
    ret a == b;
  }
}

Author comment

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: 285 / 598
Version history: 4 change(s)
Referenced in: #1034167 - Standard Classes + Interfaces (LIVE, continuation of #1003674)