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