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:
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user