Added Iterable<E> constructor; widened.

git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4575 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2007-02-13 19:12:17 +00:00
parent 9ac3e86fe0
commit 9c03e16434
+94 -116
View File
@@ -37,22 +37,18 @@ import com.threerings.io.Streamable;
import com.threerings.presents.Log; import com.threerings.presents.Log;
/** /**
* The distributed set class provides a means by which an unordered set of * The distributed set class provides a means by which an unordered set of objects can be
* objects can be maintained as a distributed object field. Entries can be * maintained as a distributed object field. Entries can be added to and removed from the set,
* added to and removed from the set, requests for which will generate * requests for which will generate events much like other distributed object fields.
* events much like other distributed object fields.
* *
* <p> Classes that wish to act as set entries must implement the {@link * <p> Classes that wish to act as set entries must implement the {@link Entry} interface which
* Entry} interface which extends {@link Streamable} and adds the * extends {@link Streamable} and adds the requirement that the object provide a key which will be
* requirement that the object provide a key which will be used to * used to identify entry equality. Thus an entry is declared to be in a set of the object returned
* identify entry equality. Thus an entry is declared to be in a set of * by that entry's {@link Entry#getKey} method is equal (using {@link Object#equals}) to the entry
* the object returned by that entry's {@link Entry#getKey} method is * returned by the {@link Entry#getKey} method of some other entry in the set. Additionally, in the
* equal (using {@link Object#equals}) to the entry returned by the {@link * case of entry removal, only the key for the entry to be removed will be transmitted with the
* Entry#getKey} method of some other entry in the set. Additionally, in * removal event to save network bandwidth. Lastly, the object returned by {@link Entry#getKey}
* the case of entry removal, only the key for the entry to be removed * must be a {@link Streamable} type.
* will be transmitted with the removal event to save network
* bandwidth. Lastly, the object returned by {@link Entry#getKey} must be
* a {@link Streamable} type.
*/ */
public class DSet<E extends DSet.Entry> public class DSet<E extends DSet.Entry>
implements Iterable<E>, Streamable, Cloneable implements Iterable<E>, Streamable, Cloneable
@@ -63,23 +59,36 @@ public class DSet<E extends DSet.Entry>
public static interface Entry extends Streamable public static interface Entry extends Streamable
{ {
/** /**
* Each entry provide an associated key which is used to determine * Each entry provide an associated key which is used to determine its uniqueness in the
* its uniqueness in the set. See the {@link DSet} class * set. See the {@link DSet} class documentation for further information.
* documentation for further information.
*/ */
public Comparable getKey (); public Comparable getKey ();
} }
/** /**
* Creates a distributed set and populates it with values from the * Creates a distributed set and populates it with values from the supplied iterator. This
* supplied iterator. This should be done before the set is unleashed * should be done before the set is unleashed into the wild distributed object world because no
* into the wild distributed object world because no associated entry * associated entry added events will be generated. Additionally, this operation does not check
* added events will be generated. Additionally, this operation does * for duplicates when adding entries, so one should be sure that the iterator contains only
* not check for duplicates when adding entries, so one should be sure * unique entries.
* that the iterator contains only unique entries.
* *
* @param source an iterator from which we will initially populate the * @param source an iterator from which we will initially populate the set.
* set. */
public DSet (Iterable<E> source)
{
for (E e : source) {
add(e);
}
}
/**
* Creates a distributed set and populates it with values from the supplied iterator. This
* should be done before the set is unleashed into the wild distributed object world because no
* associated entry added events will be generated. Additionally, this operation does not check
* for duplicates when adding entries, so one should be sure that the iterator contains only
* unique entries.
*
* @param source an iterator from which we will initially populate the set.
*/ */
public DSet (Iterator<E> source) public DSet (Iterator<E> source)
{ {
@@ -89,15 +98,13 @@ public class DSet<E extends DSet.Entry>
} }
/** /**
* Creates a distributed set and populates it with values from the * Creates a distributed set and populates it with values from the supplied array. This should
* supplied array. This should be done before the set is unleashed * be done before the set is unleashed into the wild distributed object world because no
* into the wild distributed object world because no associated entry * associated entry added events will be generated. Additionally, this operation does not check
* added events will be generated. Additionally, this operation does * for duplicates when adding entries, so one should be sure that the iterator contains only
* not check for duplicates when adding entries, so one should be sure * unique entries.
* that the iterator contains only unique entries.
* *
* @param source an array from which we will initially populate the * @param source an array from which we will initially populate the set.
* set.
*/ */
public DSet (E[] source) public DSet (E[] source)
{ {
@@ -124,10 +131,9 @@ public class DSet<E extends DSet.Entry>
} }
/** /**
* Returns true if the set contains an entry whose * Returns true if the set contains an entry whose <code>getKey()</code> method returns a key
* <code>getKey()</code> method returns a key that * that <code>equals()</code> the key returned by <code>getKey()</code> of the supplied
* <code>equals()</code> the key returned by <code>getKey()</code> of * entry. Returns false otherwise.
* the supplied entry. Returns false otherwise.
*/ */
public boolean contains (E elem) public boolean contains (E elem)
{ {
@@ -135,8 +141,8 @@ public class DSet<E extends DSet.Entry>
} }
/** /**
* Returns true if an entry in the set has a key that * Returns true if an entry in the set has a key that <code>equals()</code> the supplied
* <code>equals()</code> the supplied key. Returns false otherwise. * key. Returns false otherwise.
*/ */
public boolean containsKey (Comparable key) public boolean containsKey (Comparable key)
{ {
@@ -144,23 +150,20 @@ public class DSet<E extends DSet.Entry>
} }
/** /**
* Returns the entry that matches (<code>getKey().equals(key)</code>) * Returns the entry that matches (<code>getKey().equals(key)</code>) the specified key or null
* the specified key or null if no entry could be found that matches * if no entry could be found that matches the key.
* the key.
*/ */
public E get (Comparable key) public E get (Comparable key)
{ {
// determine where we'll be adding the new element // determine where we'll be adding the new element
int eidx = ArrayUtil.binarySearch( int eidx = ArrayUtil.binarySearch(_entries, 0, _size, key, ENTRY_COMP);
_entries, 0, _size, key, ENTRY_COMP);
return (eidx < 0) ? null : _entries[eidx]; return (eidx < 0) ? null : _entries[eidx];
} }
/** /**
* Returns an iterator over the entries of this set. It does not * Returns an iterator over the entries of this set. It does not support modification (nor
* support modification (nor iteration while modifications are being * iteration while modifications are being made to the set). It should not be kept around as it
* made to the set). It should not be kept around as it can quickly * can quickly become out of date.
* become out of date.
* *
* @deprecated * @deprecated
*/ */
@@ -171,10 +174,9 @@ public class DSet<E extends DSet.Entry>
} }
/** /**
* Returns an iterator over the entries of this set. It does not * Returns an iterator over the entries of this set. It does not support modification (nor
* support modification (nor iteration while modifications are being * iteration while modifications are being made to the set). It should not be kept around as it
* made to the set). It should not be kept around as it can quickly * can quickly become out of date.
* become out of date.
*/ */
public Iterator<E> iterator () public Iterator<E> iterator ()
{ {
@@ -203,10 +205,9 @@ public class DSet<E extends DSet.Entry>
throw new ConcurrentModificationException(); throw new ConcurrentModificationException();
} }
if (_ssize != _size) { if (_ssize != _size) {
Log.warning("Size changed during iteration " + Log.warning("Size changed during iteration [ssize=" + _ssize +
"[ssize=" + _ssize + ", nsize=" + _size + ", nsize=" + _size +
", entsries=" + StringUtil.toString(_entries) + ", entsries=" + StringUtil.toString(_entries) + "].");
"].");
Thread.dumpStack(); Thread.dumpStack();
} }
} }
@@ -217,12 +218,10 @@ public class DSet<E extends DSet.Entry>
} }
/** /**
* Copies the elements of this distributed set into the supplied * Copies the elements of this distributed set into the supplied array. If the array is not
* array. If the array is not large enough to hold all of the * large enough to hold all of the elements, as many as fit into the array will be copied. If
* elements, as many as fit into the array will be copied. If the * the <code>array</code> argument is null, an object array of sufficient size to contain all
* <code>array</code> argument is null, an object array of sufficient * of the elements of this set will be created and returned.
* size to contain all of the elements of this set will be created and
* returned.
*/ */
public E[] toArray (E[] array) public E[] toArray (E[] array)
{ {
@@ -245,24 +244,20 @@ public class DSet<E extends DSet.Entry>
} }
/** /**
* Adds the specified entry to the set. This should not be called * Adds the specified entry to the set. This should not be called directly, instead the
* directly, instead the associated <code>addTo{Set}()</code> method * associated <code>addTo{Set}()</code> method should be called on the distributed object that
* should be called on the distributed object that contains the set in * contains the set in question.
* question.
* *
* @return true if the entry was added, false if it was already in * @return true if the entry was added, false if it was already in the set.
* the set.
*/ */
protected boolean add (E elem) protected boolean add (E elem)
{ {
// determine where we'll be adding the new element // determine where we'll be adding the new element
int eidx = ArrayUtil.binarySearch( int eidx = ArrayUtil.binarySearch(_entries, 0, _size, elem, ENTRY_COMP);
_entries, 0, _size, elem, ENTRY_COMP);
// if the element is already in the set, bail now // if the element is already in the set, bail now
if (eidx >= 0) { if (eidx >= 0) {
Log.warning("Refusing to add duplicate entry [entry=" + elem + Log.warning("Refusing to add duplicate entry [entry=" + elem + ", set=" + this + "].");
", set=" + this + "].");
return false; return false;
} }
@@ -274,20 +269,17 @@ public class DSet<E extends DSet.Entry>
if (_size >= elength) { if (_size >= elength) {
// sanity check // sanity check
if (elength > 2048) { if (elength > 2048) {
Log.warning("Requested to expand to questionably large size " + Log.warning("Requested to expand to questionably large size [l=" + elength + "].");
"[length=" + elength + "].");
Thread.dumpStack(); Thread.dumpStack();
} }
// create a new array and copy our data into it // create a new array and copy our data into it
@SuppressWarnings("unchecked") E[] elems = @SuppressWarnings("unchecked") E[] elems = (E[])new Entry[elength*2];
(E[])new Entry[elength*2];
System.arraycopy(_entries, 0, elems, 0, elength); System.arraycopy(_entries, 0, elems, 0, elength);
_entries = elems; _entries = elems;
} }
// if the entry doesn't go at the end, shift the elements down to // if the entry doesn't go at the end, shift the elements down to accomodate it
// accomodate it
if (eidx < _size) { if (eidx < _size) {
System.arraycopy(_entries, eidx, _entries, eidx+1, _size-eidx); System.arraycopy(_entries, eidx, _entries, eidx+1, _size-eidx);
} }
@@ -301,13 +293,11 @@ public class DSet<E extends DSet.Entry>
} }
/** /**
* Removes the specified entry from the set. This should not be called * Removes the specified entry from the set. This should not be called directly, instead the
* directly, instead the associated <code>removeFrom{Set}()</code> * associated <code>removeFrom{Set}()</code> method should be called on the distributed object
* method should be called on the distributed object that contains the * that contains the set in question.
* set in question.
* *
* @return true if the entry was removed, false if it was not in the * @return true if the entry was removed, false if it was not in the set.
* set.
*/ */
protected boolean remove (E elem) protected boolean remove (E elem)
{ {
@@ -315,13 +305,11 @@ public class DSet<E extends DSet.Entry>
} }
/** /**
* Removes from the set the entry whose key matches the supplied * Removes from the set the entry whose key matches the supplied key. This should not be called
* key. This should not be called directly, instead the associated * directly, instead the associated <code>removeFrom{Set}()</code> method should be called on
* <code>removeFrom{Set}()</code> method should be called on the * the distributed object that contains the set in question.
* distributed object that contains the set in question.
* *
* @return the old matching entry if found and removed, null if not * @return the old matching entry if found and removed, null if not found.
* found.
*/ */
protected E removeKey (Comparable key) protected E removeKey (Comparable key)
{ {
@@ -333,19 +321,16 @@ public class DSet<E extends DSet.Entry>
} }
// look up this entry's position in our set // look up this entry's position in our set
int eidx = ArrayUtil.binarySearch( int eidx = ArrayUtil.binarySearch(_entries, 0, _size, key, ENTRY_COMP);
_entries, 0, _size, key, ENTRY_COMP);
// if we found it, remove it // if we found it, remove it
if (eidx >= 0) { if (eidx >= 0) {
// extract the old entry // extract the old entry
E oldEntry = _entries[eidx]; E oldEntry = _entries[eidx];
_size--; _size--;
if ((_entries.length > INITIAL_CAPACITY) && if ((_entries.length > INITIAL_CAPACITY) && (_size < _entries.length/8)) {
(_size < _entries.length/8)) {
// if we're using less than 1/8 of our capacity, shrink by half // if we're using less than 1/8 of our capacity, shrink by half
@SuppressWarnings("unchecked") E[] newEnts = @SuppressWarnings("unchecked") E[] newEnts = (E[])new Entry[_entries.length/2];
(E[])new Entry[_entries.length/2];
System.arraycopy(_entries, 0, newEnts, 0, eidx); System.arraycopy(_entries, 0, newEnts, 0, eidx);
System.arraycopy(_entries, eidx+1, newEnts, eidx, _size-eidx); System.arraycopy(_entries, eidx+1, newEnts, eidx, _size-eidx);
_entries = newEnts; _entries = newEnts;
@@ -364,20 +349,18 @@ public class DSet<E extends DSet.Entry>
} }
/** /**
* Updates the specified entry by locating an entry whose key matches * Updates the specified entry by locating an entry whose key matches the key of the supplied
* the key of the supplied entry and overwriting it. This should not * entry and overwriting it. This should not be called directly, instead the associated
* be called directly, instead the associated * <code>update{Set}()</code> method should be called on the distributed object that contains
* <code>update{Set}()</code> method should be called on the * the set in question.
* distributed object that contains the set in question.
* *
* @return the old entry that was replaced, or null if it was not * @return the old entry that was replaced, or null if it was not found (in which case nothing
* found (in which case nothing is updated). * is updated).
*/ */
protected E update (E elem) protected E update (E elem)
{ {
// look up this entry's position in our set // look up this entry's position in our set
int eidx = ArrayUtil.binarySearch( int eidx = ArrayUtil.binarySearch(_entries, 0, _size, elem, ENTRY_COMP);
_entries, 0, _size, elem, ENTRY_COMP);
// if we found it, update it // if we found it, update it
if (eidx >= 0) { if (eidx >= 0) {
@@ -396,10 +379,8 @@ public class DSet<E extends DSet.Entry>
public DSet<E> typedClone () public DSet<E> typedClone ()
{ {
try { try {
@SuppressWarnings("unchecked") DSet<E> nset = @SuppressWarnings("unchecked") DSet<E> nset = (DSet<E>)super.clone();
(DSet<E>)super.clone(); @SuppressWarnings("unchecked") E[] copy = (E[])new Entry[_entries.length];
@SuppressWarnings("unchecked") E[] copy =
(E[])new Entry[_entries.length];
nset._entries = copy; nset._entries = copy;
System.arraycopy(_entries, 0, nset._entries, 0, _entries.length); System.arraycopy(_entries, 0, nset._entries, 0, _entries.length);
nset._modCount = 0; nset._modCount = 0;
@@ -465,8 +446,7 @@ public class DSet<E extends DSet.Entry>
} }
/** The entries of the set (in a sparse array). */ /** The entries of the set (in a sparse array). */
@SuppressWarnings("unchecked") protected E[] _entries = @SuppressWarnings("unchecked") protected E[] _entries = (E[])new Entry[INITIAL_CAPACITY];
(E[])new Entry[INITIAL_CAPACITY];
/** The number of entries in this set. */ /** The number of entries in this set. */
protected int _size; protected int _size;
@@ -480,10 +460,8 @@ public class DSet<E extends DSet.Entry>
/** Used for lookups and to keep the set contents sorted on insertions. */ /** Used for lookups and to keep the set contents sorted on insertions. */
protected static Comparator<Object> ENTRY_COMP = new Comparator<Object>() { protected static Comparator<Object> ENTRY_COMP = new Comparator<Object>() {
public int compare (Object o1, Object o2) { public int compare (Object o1, Object o2) {
Comparable c1 = (o1 instanceof Entry) ? Comparable c1 = (o1 instanceof Entry) ? ((Entry)o1).getKey() : (Comparable)o1;
((Entry)o1).getKey() : (Comparable)o1; Comparable c2 = (o2 instanceof Entry) ? ((Entry)o2).getKey() : (Comparable)o2;
Comparable c2 = (o2 instanceof Entry) ?
((Entry)o2).getKey() : (Comparable)o2;
@SuppressWarnings("unchecked") int val = c1.compareTo(c2); @SuppressWarnings("unchecked") int val = c1.compareTo(c2);
return val; return val;
} }