I've been messing with IntSets. I won't check in my more esoteric classes

(RangeIntSet, CompleteIntSet & ClumpyArrayIntSet (which both are capable
of holding every int, and not maxing out at Integer.MAX_VALUE size)).
But here's a bunch of useful stuff all wrapped up nicely.


git-svn-id: https://samskivert.googlecode.com/svn/trunk@2690 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
ray.j.greenwell
2010-01-08 18:52:36 +00:00
parent 61609ffcd5
commit 503d57d7de
+490
View File
@@ -0,0 +1,490 @@
//
// $Id$
package com.samskivert.util;
import java.util.Arrays;
import java.util.Collection;
import java.util.NoSuchElementException;
/**
* Utility methods for working with IntSets.
*/
public class IntSets
{
/** Uninstantiable. */
private IntSets () {}
/**
* Create a new IntSet, initially empty.
*/
public static IntSet create ()
{
return new ArrayIntSet();
}
/**
* Create a new IntSet, initially containing the specified ints.
*/
public static IntSet create (int... ints)
{
checkNotNull(ints);
return new ArrayIntSet(ints);
}
/**
* Create a new IntSet, initially containing the ints in the specified IntSet.
*/
public static IntSet create (IntSet from)
{
checkNotNull(from);
return new ArrayIntSet(from);
}
/**
* Create a new IntSet, initially containing the ints in the specified Collection<Integer>.
*/
public static IntSet create (Collection<Integer> from)
{
checkNotNull(from);
return new ArrayIntSet(from);
}
/**
* Return an <b>immutable</b> empty IntSet.
*/
public static IntSet emptyIntSet ()
{
return EMPTY_INTSET;
}
/**
* Return an unmodifiable view of the specified IntSet
*/
public static IntSet unmodifiableIntSet (IntSet s)
{
checkNotNull(s);
return new UnmodifiableIntSetView(s);
}
/**
* Create a new IntSet containing the ints that are contained in <b>all</b>
* of the specified sets. The returned set may be further modified per your needs.
*/
public static IntSet and (IntSet... sets)
{
checkNotNull(sets);
IntSet result = create();
int len = sets.length;
if (len > 0) {
OUTER:
for (Interator it = sets[0].interator(); it.hasNext(); ) {
int val = it.nextInt();
for (int ii = 1; ii < len; ii++) {
if (!sets[ii].contains(val)) {
continue OUTER;
}
}
result.add(val);
}
}
return result;
}
/**
* Returns an <b>immutable</b> view of the underlying sets.
*/
public static IntSet andView (IntSet... sets)
{
checkNotNull(sets);
return new AndIntSetView(sets);
}
/**
* Create a new IntSet containing the ints that are contained in <b>any</b>
* of the specified sets. The returned set may be further modified per your needs.
*/
public static IntSet or (IntSet... sets)
{
checkNotNull(sets);
IntSet result = create();
for (IntSet set : sets) {
result.addAll(set);
}
return result;
}
/**
* Returns an <b>immutable</b> view of the underlying sets.
*/
public static IntSet orView (IntSet... sets)
{
checkNotNull(sets);
return new OrIntSetView(sets);
}
/**
* Creates a new IntSet, initially populated with ints contained in set1 but not in set2.
* Set2 may also contain elements not present in set1, these are ignored.
*/
public static IntSet difference (IntSet set1, IntSet set2)
{
return and(set1, notView(set2));
}
/**
* Returns an <b>immutable</b> view containing the ints contained in set1 but not in set2.
* Set2 may also contain elements not present in set1, these are ignored.
*/
public static IntSet differenceView (IntSet set1, IntSet set2)
{
return andView(set1, notView(set2));
}
/**
* Returns a <b>immutable</b> view of the underlying set.
*
* TODO: not yet public because iteration and size are weird.
*/
protected static IntSet notView (IntSet set)
{
checkNotNull(set);
// TODO
return new NotIntSetView(set);
}
/**
* Validate the specified argument.
*/
protected static void checkNotNull (Object o)
{
if (o == null) {
throw new NullPointerException();
}
}
/**
* Validate the specified arguments.
*/
protected static void checkNotNull (Object[] array)
{
checkNotNull((Object)array);
for (Object o : array) {
checkNotNull(o);
}
}
/** The immutable empty IntSet. */
protected static final IntSet EMPTY_INTSET = new EmptyIntSet();
/**
* An extension to AbstractIntSet that implements a "true" immutable IntSet.
* That is, calling removeAll() with an empty Collection will still throw an Exception.
*/
protected static abstract class AbstractImmutableIntSet extends AbstractIntSet
{
// the following are overridden so that they don't appear to work if given empty collections
// or an improper Object key
@Override public boolean remove (Object o) { throw new UnsupportedOperationException(); }
@Override public boolean addAll (Collection<? extends Integer> c)
{
throw new UnsupportedOperationException();
}
@Override public boolean removeAll (Collection<?> c)
{
throw new UnsupportedOperationException();
}
@Override public boolean retainAll (Collection<?> c)
{
throw new UnsupportedOperationException();
}
@Override public void clear ()
{
throw new UnsupportedOperationException();
}
} // end: class AbstractImmutableIntSet
/**
* The empty IntSet.
*/
protected static class EmptyIntSet extends AbstractImmutableIntSet
{
@Override public boolean contains (int value) { return false; }
@Override public int size () { return 0; }
@Override
public Interator interator ()
{
return EMPTY_INTERATOR;
}
protected static Interator EMPTY_INTERATOR =
new AbstractInterator() {
public boolean hasNext () { return false; }
public int nextInt () { throw new NoSuchElementException(); }
};
} // end: class EmptyIntSet
/**
* An immutable view of another IntSet.
*/
protected static class UnmodifiableIntSetView extends AbstractImmutableIntSet
{
public UnmodifiableIntSetView (IntSet s)
{
_s = s;
}
@Override public int size () { return _s.size(); }
@Override public boolean contains (int value) { return _s.contains(value); }
public Interator interator ()
{
return new AbstractInterator() {
public boolean hasNext () { return _i.hasNext(); }
public int nextInt () { return _i.nextInt(); }
protected Interator _i = _s.interator();
};
}
protected IntSet _s;
} // end: class UnmodifiableIntSetView
/**
* A building-block Interator that utilizes a findNext() method to locate the next int
* to return.
*/
protected static abstract class FindingInterator extends AbstractInterator
{
@Override public boolean hasNext ()
{
return _hasNext || (_hasNext = findNext());
}
@Override public int nextInt ()
{
if (_hasNext) {
_hasNext = false;
} else if (!findNext()) { // support calling nextInt() without using hasNext() (tsk tsk)
throw new NoSuchElementException();
}
return _next;
}
/**
* Populate _next with the next value and return true, or return false if there are
* no more ints in this Interator.
*/
protected abstract boolean findNext ();
/** Does _next contain a valid value? */
protected boolean _hasNext;
/** The next value to return, iff _hasNext is true. */
protected int _next;
} // end: class FindingInterator
/**
* An "and" view of the specified sources.
*/
protected static class AndIntSetView extends AbstractImmutableIntSet
{
public AndIntSetView (IntSet... sources)
{
if (sources.length == 0) {
_sources = new IntSet[] { IntSets.emptyIntSet() };
} else {
// TODO: sort sources by size (smallest first) to optimize?
// TODO: copy to prevent befuckery?
_sources = sources;
}
}
@Override
public boolean contains (int value)
{
for (IntSet src : _sources) {
if (!src.contains(value)) {
return false;
}
}
return true;
}
// TODO: smarter size() (Right now we're using inherited, which counts interated)
public Interator interator ()
{
return new FindingInterator() {
@Override protected boolean findNext ()
{
OUTER:
while (_i.hasNext()) {
_next = _i.nextInt();
// make sure it's in all the other sets
for (int ii = 1; ii < _sources.length; ii++) {
if (!_sources[ii].contains(_next)) {
continue OUTER;
}
}
return true;
}
return false;
}
protected Interator _i = _sources[0].interator();
};
}
protected IntSet[] _sources;
} // end: class AndIntSetView
/**
* An "or" view of the specified sources.
*/
protected static class OrIntSetView extends AbstractImmutableIntSet
{
public OrIntSetView (IntSet... sources)
{
_sources = sources;
// TODO: sort sources by size (largest first), to optimize general checks?
// TODO: copy array to prevent befuckery?
}
@Override
public boolean contains (int value)
{
for (IntSet src : _sources) {
if (src.contains(value)) {
return true;
}
}
return false;
}
// TODO: smarter size() (right now we're using inherited, which
public Interator interator ()
{
return new FindingInterator() {
protected boolean findNext ()
{
while (true) {
if (_i == null) {
if (_srcIdx >= _sources.length - 1) {
return false;
}
_i = _sources[++_srcIdx].interator();
}
OUTER: while (_i.hasNext()) {
_next = _i.nextInt();
// make sure it's not in any of the previous sources
for (int ii = 0; ii < _srcIdx; ii++) {
if (_sources[ii].contains(_next)) {
continue OUTER;
}
}
return true;
}
_i = null;
}
}
protected Interator _i;
protected int _srcIdx = -1;
};
}
protected IntSet[] _sources;
} // end: class OrIntSetView
/**
* An "or" view of the specified sources.
*/
protected static class NotIntSetView extends AbstractImmutableIntSet
{
/**
* Construct a ComplementIntSet with the specified view source.
*/
public NotIntSetView (IntSet source)
{
_source = source;
}
@Override
public int size ()
{
// Even if our source was an ArrayIntSet with exactly MAX_VALUE elements,
// there are still over MAX_VALUE ints in the complement!
if (_source.size() < Integer.MAX_VALUE) {
return Integer.MAX_VALUE;
}
// else: iterate
return super.size();
}
@Override // from IntSet
public boolean contains (int value)
{
return !_source.contains(value);
}
/**
* Returns the positive integers first, starting with 0.
*/
// from IntSet
public Interator interator ()
{
return new FindingInterator() {
protected boolean findNext ()
{
if (_positive) {
// first go from 0 to MAX_VALUE
while (_next != Integer.MAX_VALUE) {
if (contains(++_next)) {
return true;
}
}
// prepare to go negative.. (preparing to go negative, sir!)
_positive = false;
_next = 0;
}
// now go down from -1 to MIN_VALUE
while (_next != Integer.MIN_VALUE) {
if (contains(--_next)) {
return true;
}
}
return false;
}
{ // initializer
_next = -1;
}
protected boolean _positive = true;
};
}
@Override // from IntSet
public int[] toIntArray ()
{
Interator it = interator();
if (!it.hasNext()) {
return new int[0];
}
int[] array = new int[Integer.MAX_VALUE];
int index = 0;
do {
array[index++] = it.nextInt();
} while ((index < Integer.MAX_VALUE) && it.hasNext());
// we may need to trim the array down to size
return (index == Integer.MAX_VALUE) ? array : Arrays.copyOf(array, index);
}
/** The ints we <b>don't</b> contain. */
protected IntSet _source;
} // end: class NotIntSetView
}