/* * Copyright 2009-2016 Tilmann Zaeschke. All rights reserved. * * This file is part of ZooDB. * * ZooDB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ZooDB is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ZooDB. If not, see <http://www.gnu.org/licenses/>. * * See the README and COPYING files for further information. */ package org.zoodb.internal.util; import java.util.Collection; import java.util.Enumeration; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Set; /** * This class is an implementation of the Set interface. To check object * equality, it uses '==' instead of 'equals()'. * * @param <E> * @author Tilmann Zaeschke */ public final class ObjectIdentitySet<E> implements Set<E> { private ObjectIdentitySetEntry<E>[] table; private int count; private int threshold; private static final float LOAD_FACTOR = 0.75f; private static final int INITIAL_CAPACITY = 127; /** * Create a new ObjectIdentitySet. */ public ObjectIdentitySet () { this (INITIAL_CAPACITY); } @SuppressWarnings("unchecked") public ObjectIdentitySet (int initial_capacity) { if (initial_capacity > INITIAL_CAPACITY) { //First get a good number: (2^n)-1 > initial_capacity //Ideally, n would be a prime, but that's getting too //difficult here. int p = 1; while (initial_capacity > 1) { initial_capacity = initial_capacity >> 2; p++; } if (p%2 == 0) { p++; } initial_capacity = (int) Math.pow(2, p) - 1; } else { //Go for minimal feasible size (expecting resizing) initial_capacity = INITIAL_CAPACITY; } int initial_size = (int) (initial_capacity / LOAD_FACTOR); table = new ObjectIdentitySetEntry [initial_size]; count = 0; threshold = initial_capacity; } /** * @param object * @return Returns <tt>true</tt> if the object was added, otherwise * <tt>false</tt>. * @see java.util.Set#add(java.lang.Object) */ public final boolean add (E object) { int hash = System.identityHashCode (object); int index = (hash & 0x7FFFFFFF) % table.length; ObjectIdentitySetEntry<E> head = table [index]; for (ObjectIdentitySetEntry<E> e = head; e != null; e = e.next) { if (object == e.object) return false; } table [index] = new ObjectIdentitySetEntry<E> (object, head); count++; if (count >= threshold) rehash (count * 2 + 1); return true; } final void add (E[] objects) { for (E obj: objects) { add (obj); } } /** * @see java.util.Set#contains(java.lang.Object) */ public final boolean contains (Object object) { int hash = System.identityHashCode (object); int index = (hash & 0x7FFFFFFF) % table.length; ObjectIdentitySetEntry<E> e; for (e = table [index]; e != null; e = e.next) { if (object == e.object) return true; } return false; } @SuppressWarnings("unchecked") final void rehash (int new_length) { ObjectIdentitySetEntry<E>[] old_table = table; int old_length = old_table.length; //int new_length = old_length * 2 + 1; ObjectIdentitySetEntry<E>[] new_table = new ObjectIdentitySetEntry [new_length]; for (int i = 0; i < old_length; i++) { ObjectIdentitySetEntry<E> next; ObjectIdentitySetEntry<E> e; for (e = old_table [i]; e != null; e = next) { int hash = System.identityHashCode (e.object); int new_index = ((hash & 0x7FFFFFFF) % new_length); next = e.next; e.next = new_table [new_index]; new_table [new_index] = e; } } table = new_table; threshold = (int) (new_length * LOAD_FACTOR); } /** * @see java.util.Set#size() */ public final int size() { return count; } /** * @see java.util.Set#clear() */ @SuppressWarnings("unchecked") public final void clear() { threshold = INITIAL_CAPACITY; count = 0; table = new ObjectIdentitySetEntry [(int) (threshold / LOAD_FACTOR)]; } /** * @see java.util.Set#isEmpty() */ public final boolean isEmpty() { return count == 0; } /** * @see java.util.Set#toArray() */ public final Object[] toArray() { Object[] a = new Object[count]; int j = 0; for (Iterator<E> i = this.iterator(); i.hasNext(); ) { a[j++] = i.next(); } return a; } /** * @see java.util.Set#remove(java.lang.Object) */ public final boolean remove(Object obj) { int hash = System.identityHashCode (obj); int index = (hash & 0x7FFFFFFF) % table.length; ObjectIdentitySetEntry<E> e; ObjectIdentitySetEntry<E> prev = null; for (e = table [index]; e != null; e = e.next) { if (obj == e.object) { //delete it if (prev == null) { //first element table [index] = e.next; } else { prev.next = e.next; } count--; // rehash if < 25% filled and size > 127 if (count < threshold*(1-LOAD_FACTOR) && count > INITIAL_CAPACITY) rehash (count / 2 + 1); return true; } prev = e; } return false; } /** * @see java.util.Set#addAll(java.util.Collection) */ public final boolean addAll(Collection<? extends E> c) { boolean ret = false; for (E o: c) { ret |= add(o); } return ret; } /** * Add all elements of in the enumeration * @param en */ public final void addAll(Enumeration<E> en) { while (en.hasMoreElements()) { add(en.nextElement()); } } /** * @see java.util.Set#containsAll(java.util.Collection) */ public final boolean containsAll(Collection<?> c) { if (c == null) { throw new NullPointerException(); } throw new UnsupportedOperationException(); } /** * @see java.util.Set#removeAll(java.util.Collection) */ public final boolean removeAll(Collection<?> c) { if (c == null) { throw new NullPointerException(); } boolean hasChanged = false; for (Object o: c) { if (remove(o)) hasChanged = true; } return hasChanged; } /** * @see java.util.Set#retainAll(java.util.Collection) */ public final boolean retainAll(Collection<?> c) { if (c == null) { throw new NullPointerException(); } throw new UnsupportedOperationException(); } /** * @see java.util.Set#iterator() */ public final Iterator<E> iterator() { return new ObjectIdentitySetIterator(); } /** * @param a * @param <T> * @return Array representation of this container * @see java.util.Set#toArray(Object[]) */ public final <T> T[] toArray(T[] a) { if (a == null) { throw new NullPointerException(); } throw new UnsupportedOperationException(); } /** * @see java.lang.Object#toString() */ public final String toString() { StringBuffer buf = new StringBuffer(); buf.append("ObjectIdentitySet(" + count + "): "); for (Iterator<E> i = this.iterator(); i.hasNext(); ) { buf.append(i.next() + "; "); } return buf.toString(); } private final static class ObjectIdentitySetEntry<T> { private final T object; private ObjectIdentitySetEntry<T> next; ObjectIdentitySetEntry (T object1, ObjectIdentitySetEntry<T> next1) { object = object1; next = next1; } } private final class ObjectIdentitySetIterator implements Iterator<E> { private int pos = 0; private ObjectIdentitySetEntry<E> next = null; /** * @see java.util.Iterator#remove() */ public final void remove() { throw new UnsupportedOperationException(); } /** * @see java.util.Iterator#hasNext() */ public final boolean hasNext() { if (next == null) { for( ; pos < table.length; pos++) { if (table[pos] != null) { next = table[pos]; pos++; return true; } } return false; } return true; } /** * @see java.util.Iterator#next() */ public final E next() { if (!hasNext()) { throw new NoSuchElementException(); } ObjectIdentitySetEntry<E> n = next; next = next.next; return n.object; } } }