1919import java .util .Arrays ;
2020
2121/**
22- * A class for allocating integer IDs in a given range.
22+ * A class for allocating integers from a given range.
23+ * <p/><strong>Concurrent Semantics:</strong><br />
24+ * This class is <b><i>not</i></b> thread safe.
25+ *
26+ * <p/><b>Implementation notes:</b>
27+ * <br/>This could really use being a balanced binary tree. However for normal
28+ * usages it doesn't actually matter.
29+ *
30+ * <p/><b>Invariants:</b>
31+ * <br/>Sorted in order of first element.
32+ * <br/>Intervals are non-overlapping, non-adjacent.
33+ *
2334 */
24- public class IntAllocator {
35+ public class IntAllocator {
2536
26- // Invariant: Sorted in order of first element.
27- // Invariant: Intervals are non-overlapping, non-adjacent.
28- // This could really use being a balanced binary tree. However for normal
29- // usages it doesn't actually matter.
3037 private IntervalList base ;
3138
3239 private final int [] unsorted ;
3340 private int unsortedCount = 0 ;
3441
3542 /**
36- * A class representing an inclusive interval from start to end.
43+ * A node in a singly-linked list of inclusive intervals.
44+ * <br/>A single node denotes the interval of integers from
45+ * <code>start</code> to <code>end</code> inclusive.
3746 */
3847 private static class IntervalList {
48+ int start ;
49+ int end ;
50+ IntervalList next ; // next interval in the list.
51+
3952 IntervalList (int start , int end ){
4053 this .start = start ;
4154 this .end = end ;
4255 }
4356
44- int start ;
45- int end ;
46- IntervalList next ;
47-
4857 int length (){ return end - start + 1 ; }
4958 }
5059
51- /** Destructively merge two IntervalLists.
52- * Invariant: None of the Intervals in the two lists may overlap
53- * intervals in this list.
54- */
55- public static IntervalList merge (IntervalList x , IntervalList y ){
56- if (x == null ) return y ;
57- if (y == null ) return x ;
58-
59- if (x .end > y .start ) return merge (y , x );
60-
61- // We now have x, y non-null and x.End < y.Start.
62- if (y .start == x .end + 1 ){
63- // The two intervals adjoin. Merge them into one and then
64- // merge the tails.
65- x .end = y .end ;
66- x .next = merge (x .next , y .next );
67- return x ;
60+ private static class IntervalListAccumulator {
61+ /** <b>Invariant:</b> either both null, or both non-null. */
62+ private IntervalList accumListStart , accumListEnd = null ;
63+
64+ /**
65+ * Add a single node to the end of the accumulated list, merging the
66+ * last two nodes if abutting.<br/>
67+ * <b>Note:</b> the node added is not modified by add;
68+ * in particular, the <code>next</code> field is preserved,
69+ * and copied if the nodes are merged.
70+ * @param iListNode node to add, terminate list if this is null.
71+ */
72+ public void add (IntervalList iListNode ) {
73+ if (accumListStart == null ) {
74+ accumListStart = accumListEnd = iListNode ;
75+ } else {
76+ if (iListNode == null ) {
77+ accumListEnd .next = null ;
78+ } else if (accumListEnd .end + 1 == iListNode .start ) {
79+ accumListEnd .end = iListNode .end ;
80+ accumListEnd .next = iListNode .next ;
81+ } else {
82+ accumListEnd .next = iListNode ;
83+ accumListEnd = iListNode ;
84+ }
85+ }
86+ }
87+ /**
88+ * Append the list from this node and return the accumulated list
89+ * including this node chain.<br/>
90+ * <b>Note:</b> The accumulated list is cleared after this call.
91+ * @param iListNode chain to append to accumListEnd.
92+ * @return the accumulated list
93+ */
94+ public IntervalList getAppendedResult (IntervalList iListNode ) {
95+ add (iListNode ); // note: this preserves the chain
96+ IntervalList result = accumListStart ;
97+ accumListStart = accumListEnd = null ;
98+ return result ;
6899 }
69-
70- // y belongs in the tail of x.
71-
72- x .next = merge (y , x .next );
73- return x ;
74100 }
75101
102+ /**
103+ * Merge two IntervalLists.
104+ * <p/><b>Preconditions:</b><br/>
105+ * None of the intervals in the two lists overlap.
106+ */
107+ private static IntervalList merge (IntervalList x , IntervalList y ){
108+ IntervalListAccumulator outList = new IntervalListAccumulator ();
109+ while (true ) {
110+ if (x == null ) { return outList .getAppendedResult (y ); }
111+ if (y == null ) { return outList .getAppendedResult (x ); }
112+ if (x .start < y .start ) {
113+ outList .add (x );
114+ x = x .next ;
115+ } else {
116+ outList .add (y );
117+ y = y .next ;
118+ }
119+ }
120+ }
76121
77- public static IntervalList fromArray (int [] xs , int length ){
122+ private static IntervalList fromArray (int [] xs , int length ){
78123 Arrays .sort (xs , 0 , length );
79124
80125 IntervalList result = null ;
@@ -100,14 +145,13 @@ public static IntervalList fromArray(int[] xs, int length){
100145 return result ;
101146 }
102147
103-
104148 /**
105149 * Creates an IntAllocator allocating integer IDs within the inclusive range
106150 * [start, end]
107151 */
108152 public IntAllocator (int start , int end ){
109153 if (start > end )
110- throw new IllegalArgumentException ("illegal range [" + start +
154+ throw new IllegalArgumentException ("illegal range [" + start +
111155 ", " + end + "]" );
112156
113157 // Fairly arbitrary heuristic for a good size for the unsorted set.
@@ -116,8 +160,8 @@ public IntAllocator(int start, int end){
116160 }
117161
118162 /**
119- * Allocate a fresh integer from the range, or return -1 if no more integers
120- * are available. This operation is guaranteed to run in O(1)
163+ * Allocate an unallocated integer from the range, or return -1 if no
164+ * more integers are available. This operation is guaranteed to run in O(1)
121165 */
122166 public int allocate (){
123167 if (unsortedCount > 0 ){
@@ -183,7 +227,7 @@ else if(current.start == id)
183227 return true ;
184228 }
185229
186- public void flush (){
230+ private void flush (){
187231 if (unsortedCount == 0 ) return ;
188232
189233 base = merge (base , fromArray (unsorted , unsortedCount ));
@@ -193,26 +237,24 @@ public void flush(){
193237 @ Override public String toString (){
194238 StringBuilder builder = new StringBuilder ();
195239
196- builder .append ("IntAllocator{" );
197-
198- builder .append ("intervals = [" );
240+ builder .append ("IntAllocator{intervals = [" );
199241 IntervalList it = base ;
200- while (it != null ){
242+ if (it != null ) {
201243 builder .append (it .start ).append (".." ).append (it .end );
202- if (it .next != null ) builder .append (", " );
203244 it = it .next ;
204245 }
205- builder .append ("]" );
206-
207- builder .append (", unsorted = [" );
208- for (int i = 0 ; i < unsortedCount ; i ++){
209- builder .append (unsorted [i ]);
210- if ( i < unsortedCount - 1 ) builder .append (", " );
246+ while (it != null ) {
247+ builder .append (", " ).append (it .start ).append (".." ).append (it .end );
248+ it = it .next ;
211249 }
212- builder .append ("]" );
213-
214-
215- builder .append ("}" );
250+ builder .append ("], unsorted = [" );
251+ if (unsortedCount > 0 ) {
252+ builder .append (unsorted [0 ]);
253+ }
254+ for (int i = 1 ; i < unsortedCount ; ++i ){
255+ builder .append (", " ).append (unsorted [i ]);
256+ }
257+ builder .append ("]}" );
216258 return builder .toString ();
217259 }
218260}
0 commit comments