/*
* FastSet.java - This file is part of the Jakstab project.
* Copyright 2007-2015 Johannes Kinder <jk@jakstab.org>
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, see <http://www.gnu.org/licenses/>.
*/
package org.jakstab.util;
import java.util.*;
import java.io.Serializable;
/**
* A fast Set implementation that maintains its members as an ArrayList as
* long as it is small in size, and changes to a LinkedHashSet once it
* grows large enough to amortize the cost of hashing functions.
*
* @author Johannes Kinder
*/
public class FastSet<E> extends AbstractSet<E> implements Set<E>, Serializable, Worklist<E>
{
private static final long serialVersionUID = -7387536630587627888L;
private static final int SMALL_CAPACITY = 15;
private static final int LARGE_CAPACITY = 100;
private static int conversions = 0;
public static int getConversionCount() { return conversions; }
private int capacity;
private Collection<E> collection;
public FastSet(int initialCapacity) {
this.capacity = initialCapacity;
if (initialCapacity <= SMALL_CAPACITY)
collection = new ArrayList<E>(initialCapacity);
else
collection = new LinkedHashSet<E>(initialCapacity);
}
/**
* Creates a new FastSet with a small default capacity.
*/
public FastSet() {
this(SMALL_CAPACITY);
}
/**
* Create a new FastSet with the same elements as the
* given collection.
*
* @param elements the elements for the new FastSet
*/
public FastSet(final Collection<? extends E> elements) {
this(elements.size());
this.addAll(elements);
}
/**
* Creates a new FastSet with only the given element
* as the single member.
*
* @param element the element for the new FastSet
*/
public FastSet(E element) {
this(1);
this.add(element);
}
/**
* Returns the size of the set.
*
* @return The current size of the set.
*/
@Override
public final int size() {
return collection.size();
}
@Override
public final Iterator<E> iterator() {
return collection.iterator();
}
/**
* Adds an element to the set.
*
* @param e Element to add.
* @return True if the set was changed, false otherwise.
*/
@Override
public final boolean add(final E e) {
// Explicit contains check necessary since ArrayList does not enforce set property
if (collection.contains(e)) return false;
/* The first time we hit the small capacity threshold, convert the
* collection to a hashset.*/
if (collection.size() - 1 == SMALL_CAPACITY && capacity <= SMALL_CAPACITY) {
LinkedHashSet<E> newCollection = new LinkedHashSet<E>(LARGE_CAPACITY);
newCollection.addAll(collection);
collection = newCollection;
capacity = LARGE_CAPACITY;
conversions++;
}
// Returns whether the set already contains e
return collection.add(e);
}
/**
* Adds all elements of an array to this Set.
*
* @param array the array to take the elements from
* @return true if this set was changed, false otherwise
*/
public final boolean addAll(E[] array) {
if (array == null) return false;
boolean changed = false;
for (int i=0; i<array.length; i++)
changed |= this.add(array[i]);
return changed;
}
/**
* Return whether this set is empty.
*
* @return True if the set has a size of 0.
*/
@Override
public final boolean isEmpty() {
return collection.isEmpty();
}
/**
* Returns true if this set contains the specified element.
*
* @param obj Element to be looked for.
* @return true if the set contains the given element.
*/
@Override
public final boolean contains(final Object obj) {
return collection.contains(obj);
}
/**
* Removes the given element from this set if present.
*
* @param obj Element to be removed.
* @return True if the element was removed, false if it was not present.
*/
@Override
public final boolean remove(final Object obj) {
return collection.remove(obj);
}
/**
* Removes all elements from this set.
*/
public final void clear() {
collection.clear();
}
/**
* Returns a new set which is the intersection of
* this set and the other set given. Both original
* sets are unchanged by this operation.
*
* @param other the other set to intersect with this set.
* @return the intersection as a new set.
*/
public final FastSet<E> intersection(Set<? extends E> other) {
FastSet<E> tmp = new FastSet<E>(this);
tmp.retainAll(other);
return tmp;
}
/**
* Returns a new set which is the union of
* this set and the other set given. Both original
* sets are unchanged by this operation.
*
* @param other the other set to unite with this set.
* @return the union as a new set.
*/
public final FastSet<E> union(Set<? extends E> other) {
FastSet<E> tmp = new FastSet<E>(this);
tmp.addAll(other);
return tmp;
}
/**
* Picks and removes an element from the set (actually
* the first one returned by iterator).
*
* @return an element from the set.
*/
@Override
public final E pick() {
Iterator<E> i = iterator();
E element = i.next();
i.remove();
return element;
}
}