From 2a6397ddcb771f0eaaece49a0433eb295ebf441e Mon Sep 17 00:00:00 2001 From: "ray.j.greenwell" Date: Fri, 15 Jan 2010 01:48:57 +0000 Subject: [PATCH] I'm just going to commit these for posterity, but I'll remove them. Hell, maybe I should remove IntSets.java too. git-svn-id: https://samskivert.googlecode.com/svn/trunk@2702 6335cc39-0255-0410-8fd6-9bcaacd3b74c --- src/java/com/samskivert/util/ArraySet.java | 207 ++++++++++++++++ .../samskivert/util/ClumpyArrayIntSet.java | 228 ++++++++++++++++++ .../com/samskivert/util/CompleteIntSet.java | 111 +++++++++ src/java/com/samskivert/util/RangeIntSet.java | 62 +++++ 4 files changed, 608 insertions(+) create mode 100644 src/java/com/samskivert/util/ArraySet.java create mode 100644 src/java/com/samskivert/util/ClumpyArrayIntSet.java create mode 100644 src/java/com/samskivert/util/CompleteIntSet.java create mode 100644 src/java/com/samskivert/util/RangeIntSet.java diff --git a/src/java/com/samskivert/util/ArraySet.java b/src/java/com/samskivert/util/ArraySet.java new file mode 100644 index 00000000..55011d69 --- /dev/null +++ b/src/java/com/samskivert/util/ArraySet.java @@ -0,0 +1,207 @@ +// +// $Id$ + +package com.samskivert.util; + +import java.util.AbstractSet; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * A more memory-efficient Set implementation than HashSet. + */ +public class ArraySet extends AbstractSet +{ + public ArraySet () + { + this(DEFAULT_CAPACITY); + } + + public ArraySet (int initialCapacity) + { + _hashCodes = new int[initialCapacity]; + _elements = new Object[initialCapacity]; + } + + public ArraySet (Collection c) + { + this(c.size()); + addAll(c); + } + + @Override + public boolean contains (Object o) + { + o = wrap(o); + return (indexFor(o.hashCode(), o) >= 0); + } + + @Override + public boolean add (E e) + { + Object o = wrap(e); + int hashCode = o.hashCode(); + int index = indexFor(hashCode, o); + if (index >= 0) { + return false; // we've already got one! + } + // convert the index to the actual insertion point + index = -(index + 1); + + // possibly grow the arrays + int len = _hashCodes.length; + int[] codes = _hashCodes; + Object[] elems = _elements; + if (len == _size) { + int newLen = (len >= Integer.MAX_VALUE/2) + ? Integer.MAX_VALUE + : Math.max(DEFAULT_CAPACITY, len * 2); + _hashCodes = new int[newLen]; + System.arraycopy(codes, 0, _hashCodes, 0, index); + _elements = new Object[newLen]; + System.arraycopy(elems, 0, _elements, 0, index); + } + + // shift and insert + if (_size > index) { + System.arraycopy(codes, index, _hashCodes, index + 1, _size - index); + System.arraycopy(elems, index, _elements, index + 1, _size - index); + } + + _hashCodes[index] = hashCode; + _elements[index] = o; + _size++; + return true; + } + + @Override + public boolean remove (Object o) + { + o = wrap(o); + int hashCode = o.hashCode(); + int index = indexFor(hashCode, o); + if (index < 0) { + return false; // not found + } + removeAtIndex(index); + return true; + } + + @Override + public int size () + { + return _size; + } + + @Override + public Iterator iterator () + { + // TODO: comodification + return new Iterator() { + public boolean hasNext () + { + return (_cursor < _size); + } + + public E next () + { + if (_cursor >= _size) { + throw new NoSuchElementException(); + } + _canRemove = true; + return unwrap(_elements[_cursor++]); + } + + public void remove () + { + if (!_canRemove) { + throw new IllegalStateException(); + } + --_cursor; + removeAtIndex(_cursor); + _canRemove = false; + } + + protected int _cursor; + protected boolean _canRemove; + }; + } + + @Override + public void clear () + { + _hashCodes = new int[DEFAULT_CAPACITY]; + _elements = new Object[DEFAULT_CAPACITY]; + _size = 0; + } + + protected int indexFor (int hashCode, Object o) + { + int index = Arrays.binarySearch(_hashCodes, 0, _size, hashCode); + if (index < 0) { + return index; // not found, so return our insertion point + } + // see if we landed right on it + if (o.equals(_elements[index])) { + return index; // found it! + } + // search forward for it + for (int ii = index + 1; (ii < _size) && (_hashCodes[ii] == hashCode); ii++) { + if (o.equals(_elements[ii])) { + return ii; + } + } + // search backward for it + for (int ii = index - 1; (ii >= 0) && (_hashCodes[ii] == hashCode); ii--) { + if (o.equals(_elements[ii])) { + return ii; + } + } + // else convert our original found index into an insertion point + return -(index + 1); + } + + protected void removeAtIndex (int index) + { + _size--; + int len = _hashCodes.length; + if ((len > DEFAULT_CAPACITY) && (_size < len/8)) { + // we're using less than 1/8 of our capacity, shrink by half, omitting the removed elem + int[] codes = new int[len / 2]; + System.arraycopy(_hashCodes, 0, codes, 0, index); + System.arraycopy(_hashCodes, index + 1, codes, index, _size - index); + _hashCodes = codes; + Object[] elems = new Object[len / 2]; + System.arraycopy(_elements, 0, elems, 0, index); + System.arraycopy(_elements, index + 1, elems, index, _size - index); + _elements = elems; + + } else { + // otherwise, simply shift the elements above it downward + System.arraycopy(_hashCodes, index + 1, _hashCodes, index, _size - index); + System.arraycopy(_elements, index + 1, _elements, index, _size - index); + _elements[_size] = null; // clear loose refs, but no need to clean hashCodes + } + } + + protected Object wrap (Object o) + { + return ((o == null) ? NULL : o); + } + + @SuppressWarnings("unchecked") + protected E unwrap (Object o) + { + return ((o == NULL) ? null : (E)o); + } + + protected int _size; + protected int[] _hashCodes; + protected Object[] _elements; + + protected static final Object NULL = new Object(); + + protected static int DEFAULT_CAPACITY = 8; +} diff --git a/src/java/com/samskivert/util/ClumpyArrayIntSet.java b/src/java/com/samskivert/util/ClumpyArrayIntSet.java new file mode 100644 index 00000000..00c2d6b3 --- /dev/null +++ b/src/java/com/samskivert/util/ClumpyArrayIntSet.java @@ -0,0 +1,228 @@ +// +// $Id$ +// +// samskivert library - useful routines for java programs +// Copyright (C) 2001-2010 Michael Bayne, et al. +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.samskivert.util; + +import java.io.Serializable; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * Efficiently stores ints that are "clumped" together. + */ +public class ClumpyArrayIntSet extends AbstractIntSet + implements Cloneable, Serializable +{ + /** + */ + public ClumpyArrayIntSet () + { + _anchors = new int[DEFAULT_CAPACITY]; + _masks = new int[DEFAULT_CAPACITY]; + } + + @Override // from interface IntSet + public boolean contains (int value) + { + int index = binarySearch(value >>> 5); + return (index >= 0) && (0 != (_masks[index] & (1 << (value & 31)))); + } + + @Override // from interface IntSet + public boolean add (int value) + { + int anchor = value >>> 5; + int mask = 1 << (value & 31); + int index = binarySearch(anchor); + if (index >= 0) { + if (0 != (_masks[index] & mask)) { + return false; // already contained + } + _masks[index] |= mask; + _size++; + return true; + } + + // convert the return value into the insertion point + index += 1; + index *= -1; + + // expand the values array if necessary, leaving room for the newly added element + int alen = _anchors.length; + int[] sourceAnchors = _anchors; + int[] sourceMask = _masks; + if (alen == _anchorSize) { + int newLen = Math.max(DEFAULT_CAPACITY, alen*2); + _anchors = new int[newLen]; + System.arraycopy(sourceAnchors, 0, _anchors, 0, index); + _masks = new int[newLen]; + System.arraycopy(sourceMask, 0, _masks, 0, index); + } + + // shift and insert + if (_anchorSize > index) { + System.arraycopy(sourceAnchors, index, _anchors, index+1, _anchorSize-index); + System.arraycopy(sourceMask, index, _masks, index+1, _anchorSize-index); + } + _anchors[index] = anchor; + _masks[index] = mask; + + // increment our size + _anchorSize++; + _size++; + + return true; + } + +// @Override // from interface IntSet +// public boolean remove (int value) +// { +// int anchor = value >>> 5; +// int mask = 1 << (value & 31); +// int index = binarySearch(anchor); +// if (index < 0) { +// return false; +// } +// +// // TODO!!!!!!! HERE +// +//// _size--; +//// if ((_values.length > DEFAULT_CAPACITY) && (_size < _values.length/8)) { +//// // if we're using less than 1/8 of our capacity, shrink by half +//// int[] newVals = new int[_values.length/2]; +//// System.arraycopy(_values, 0, newVals, 0, index); +//// System.arraycopy(_values, index+1, newVals, index, _size-index); +//// _values = newVals; +//// +//// } else { +//// // shift entries past the removed one downwards +//// System.arraycopy(_values, index+1, _values, index, _size-index); +//// //_values[_size] = 0; +//// } +// return true; +// } + + // from interface IntSet + public Interator interator () + { + // TODO: rewrite, use FindingInterator + return new AbstractInterator() { + public boolean hasNext () { + if (!_hasNext) { + _hasNext = findNext(); + } + return _hasNext; + } + + public int nextInt () { + if (!_hasNext && !hasNext()) { + throw new NoSuchElementException(); + } + int mask = Integer.lowestOneBit(_curMask); + int retval = (_anchors[_pos] << 5) + Integer.numberOfTrailingZeros(mask); + _curMask &= ~mask; + _hasNext = (_curMask != 0); + return retval; + } + +// @Override public void remove () { +// if (!_canRemove) { +// throw new IllegalStateException(); +// } +// System.arraycopy(_values, _pos, _values, _pos - 1, _size - _pos); +// _pos--; +// _size--; //_values[--_size] = 0; +// _canRemove = false; +// } + + protected boolean findNext () + { + if (_pos + 1 < _anchorSize) { + _curMask = _masks[++_pos]; + return true; + } + return false; + } + + protected int _pos = -1; + protected int _curMask; + protected boolean _hasNext; + }; + } + + @Override // from AbstractSet + public int size () + { + return (_size > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int)_size; + } + + @Override // from AbstractSet + public void clear () + { + _anchorSize = 0; + _size = 0; + Arrays.fill(_masks, 0); + } + + /** + * Performs a binary search on our values array, looking for the specified value. Swiped from + * java.util.Arrays because those wankers didn't provide a means by which to + * perform a binary search on a subset of an array. + */ + protected int binarySearch (int key) + { + int low = 0; + int high = _anchorSize-1; + + while (low <= high) { + int mid = (low + high) >> 1; + int midVal = _anchors[mid]; + + if (midVal < key) { + low = mid + 1; + } else if (midVal > key) { + high = mid - 1; + } else { + return mid; // key found + } + } + + return -(low + 1); // key not found. + } + + /** An array containing the values in this set. */ + protected int[] _anchors; + + protected int[] _masks; + + protected int _anchorSize; + + /** The number of elements in this set. */ + protected long _size; + + /** The default initial capacity of this set. */ + protected static final int DEFAULT_CAPACITY = 16; + + /** Change this if the fields or inheritance hierarchy ever changes (extremely unlikely). */ + private static final long serialVersionUID = 1; +} diff --git a/src/java/com/samskivert/util/CompleteIntSet.java b/src/java/com/samskivert/util/CompleteIntSet.java new file mode 100644 index 00000000..a07465b7 --- /dev/null +++ b/src/java/com/samskivert/util/CompleteIntSet.java @@ -0,0 +1,111 @@ +// +// $Id$ + +package com.samskivert.util; + +import java.util.NoSuchElementException; + +/** + * Is able to store every single int, using sub ArrayIntSets. + */ +public class CompleteIntSet extends AbstractIntSet +{ + public CompleteIntSet () + { + for (int ii = 0; ii < 4; ii++) { + // start each with 0-length arrays, they'll grow when used + _subsets[ii] = new ArrayIntSet(0); + } + } + + @Override + public boolean contains (int value) + { + return getSet(value).contains(value); + } + + @Override + public boolean add (int value) + { + return getSet(value).add(value); + } + + @Override + public boolean remove (int value) + { + return getSet(value).remove(value); + } + + @Override + public int size () + { + long size = 0; + for (IntSet set : _subsets) { + size += set.size(); + } + return (size > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int)size; + } + + public Interator interator () + { + return new AbstractInterator() { + public boolean hasNext () + { + while (true) { + if (_current == null) { + if (_index < 4) { + _current = _subsets[_index++].interator(); + } else { + return false; + } + } + if (_current.hasNext()) { + return true; + } + _current = null; + } + } + + public int nextInt () + { + if (!hasNext()) { + throw new NoSuchElementException(); + } + _removeFrom = _current; + return _current.nextInt(); + } + + @Override public void remove () + { + if (_removeFrom == null) { + throw new IllegalStateException(); + } + _removeFrom.remove(); + _removeFrom = null; + } + + protected Interator _current; + protected Interator _removeFrom; + protected int _index = 0; + }; + } + + protected IntSet getSet (int value) + { + return _subsets[valueToIndex(value)]; + } + + protected int valueToIndex (int value) + { + // the indexes don't matter much, as long as they're distinct.. + return (value >>> 30); +// if (value < 0) { +// return (value < Integer.MIN_VALUE/2) ? 0 : 1; +// } else { +// return (value < Integer.MAX_VALUE/2) ? 2 : 3; +// } + } + + /** The subsets. 3 would work, but 4 is a nicer split... */ + protected IntSet[] _subsets = new IntSet[4]; +} diff --git a/src/java/com/samskivert/util/RangeIntSet.java b/src/java/com/samskivert/util/RangeIntSet.java new file mode 100644 index 00000000..e93f9871 --- /dev/null +++ b/src/java/com/samskivert/util/RangeIntSet.java @@ -0,0 +1,62 @@ +// +// $Id$ + +package com.samskivert.util; + +import java.util.NoSuchElementException; + +public class RangeIntSet extends AbstractIntSet +{ + /** + * Construct a set of ints in the range from low to high, inclusive. + */ + public RangeIntSet (int lowValue, int highValue) + { + if (lowValue > highValue) { + throw new IllegalArgumentException("lowValue is greater than highValue"); + } + _low = lowValue; + _high = highValue; + } + + @Override + public boolean contains (int value) + { + return (value >= _low) && (value <= _high); + } + + @Override + public int size () + { + long size = ((long) _high) - _low + 1; + return (size > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int)size; + } + + public Interator interator () + { + return new AbstractInterator() { + public boolean hasNext () + { + return _curValid; + } + + public int nextInt () + { + if (!_curValid) { + throw new NoSuchElementException(); + } + // check for the end before we increment _cur, in case _high is MAX_VALUE + if (_cur == _high) { + _curValid = false; + } + return _cur++; + } + + protected int _cur = _low; + protected boolean _curValid = true; + }; + } + + protected int _low; + protected int _high; +}