summaryrefslogtreecommitdiff
path: root/src/com/replica/replicaisland/FixedSizeArray.java
blob: 471a5da2c0a6b0ef3982c29b1db9fa5980ec2f87 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.replica.replicaisland;

import java.util.Arrays;
import java.util.Comparator;

/**
 * FixedSizeArray is an alternative to a standard Java collection like ArrayList.  It is designed
 * to provide a contiguous array of fixed length which can be accessed, sorted, and searched without
 * requiring any runtime allocation.  This implementation makes a distinction between the "capacity"
 * of an array (the maximum number of objects it can contain) and the "count" of an array
 * (the current number of objects inserted into the array).  Operations such as set() and remove()
 * can only operate on objects that have been explicitly add()-ed to the array; that is, indexes
 * larger than getCount() but smaller than getCapacity() can't be used on their own.
 * @param <T> The type of object that this array contains.
 */
public class FixedSizeArray<T> extends AllocationGuard {
    private final static int LINEAR_SEARCH_CUTOFF = 16;
    private final T[] mContents;
    private int mCount;
    private Comparator<T> mComparator;
    private boolean mSorted;
    private Sorter<T> mSorter;
    
    public FixedSizeArray(int size) {
        super();
        assert size > 0;
        // Ugh!  No generic array construction in Java.
        mContents = (T[])new Object[size];
        mCount = 0;
        mSorted = false;
        
        mSorter = new StandardSorter<T>();        
    }
    
    public FixedSizeArray(int size, Comparator<T> comparator) {
        super();
        assert size > 0;
        mContents = (T[])new Object[size];
        mCount = 0;
        mComparator = comparator;
        mSorted = false;
        
        mSorter = new StandardSorter<T>();
    }
    
    /** 
     * Inserts a new object into the array.  If the array is full, an assert is thrown and the
     * object is ignored.
     */
    public final void add(T object) {
        assert mCount < mContents.length : "Array exhausted!";
        if (mCount < mContents.length) {
            mContents[mCount] = object;
            mSorted = false;
            mCount++;
        }
    }
    
    /** 
     * Searches for an object and removes it from the array if it is found.  Other indexes in the
     * array are shifted up to fill the space left by the removed object.  Note that if
     * ignoreComparator is set to true, a linear search of object references will be performed.
     * Otherwise, the comparator set on this array (if any) will be used to find the object.
     */
    public void remove(T object, boolean ignoreComparator) {
        final int index = find(object, ignoreComparator);
        if (index != -1) {
            remove(index);
        }
    }
    
    /** 
     * Removes the specified index from the array.  Subsequent entries in the array are shifted up
     * to fill the space.
     */
    public void remove(int index) {
        assert index < mCount;
        // ugh
        if (index < mCount) {
            for (int x = index; x < mCount; x++) {
                if (x + 1 < mContents.length && x + 1 < mCount) {
                    mContents[x] = mContents[x + 1];
                } else {
                    mContents[x]  = null;
                }
            }
            mCount--;
        }
    }
    
    /**
     * Removes the last element in the array and returns it.  This method is faster than calling
     * remove(count -1);
     * @return The contents of the last element in the array.
     */
    public T removeLast() {
        T object = null;
        if (mCount > 0) {
            object = mContents[mCount - 1];
            mContents[mCount - 1] = null;
            mCount--;
        }
        return object;
    }
    
    /**
     * Swaps the element at the passed index with the element at the end of the array.  When
     * followed by removeLast(), this is useful for quickly removing array elements.
     */
    public void swapWithLast(int index) {
        if (mCount > 0 && index < mCount - 1) {
            T object = mContents[mCount - 1];
            mContents[mCount - 1] = mContents[index];
            mContents[index] = object;
            mSorted = false;
        }
    }
    
    /**
     * Sets the value of a specific index in the array.  An object must have already been added to
     * the array at that index for this command to complete.
     */
    public void set(int index, T object) {
        assert index < mCount;
        if (index < mCount) {
            mContents[index] = object; 
        }
    }
    
    /**
     * Clears the contents of the array, releasing all references to objects it contains and 
     * setting its count to zero.
     */
    public void clear() {
        for (int x = 0; x < mCount; x++) {
            mContents[x] = null;
        }
        mCount = 0;
        mSorted = false;
    }
       
    /**
     * Returns an entry from the array at the specified index.
     */
    public T get(int index) {
        assert index < mCount;
        T result = null;
        if (index < mCount && index >= 0) {
            result = mContents[index];
        }
        return result;
    }
    
    /** 
     * Returns the raw internal array.  Exposed here so that tight loops can cache this array
     * and walk it without the overhead of repeated function calls.  Beware that changing this array
     * can leave FixedSizeArray in an undefined state, so this function is potentially dangerous
     * and should be used in read-only cases.
     * @return The internal storage array.
     */
    public final Object[] getArray() {
        return mContents;
    }
    
    
    /**
     * Searches the array for the specified object.  If the array has been sorted with sort(),
     * and if no other order-changing events have occurred since the sort (e.g. add()), a
     * binary search will be performed.  If a comparator has been specified with setComparator(),
     * it will be used to perform the search.  If not, the default comparator for the object type
     * will be used.  If the array is unsorted, a linear search is performed.
     * Note that if ignoreComparator is set to true, a linear search of object references will be 
     * performed. Otherwise, the comparator set on this array (if any) will be used to find the
     * object.
     * @param object  The object to search for.
     * @return  The index of the object in the array, or -1 if the object is not found.
     */
    public int find(T object, boolean ignoreComparator) {
        int index = -1;
        final int count = mCount;
    	final boolean sorted = mSorted;
    	final Comparator comparator = mComparator;
    	final T[] contents = mContents;
        if (sorted && !ignoreComparator && count > LINEAR_SEARCH_CUTOFF) {
            if (comparator != null) {
                index = Arrays.binarySearch(contents, object, comparator);
            } else {
                index = Arrays.binarySearch(contents, object);
            }
            // Arrays.binarySearch() returns a negative insertion index if the object isn't found,
            // but we just want a boolean.
            if (index < 0) {
                index = -1;
            }
        } else {
            // unsorted, linear search
        	
            if (comparator != null && !ignoreComparator) {
                for (int x = 0; x < count; x++) {
                	final int result = comparator.compare(contents[x], object);
                    if (result == 0) {
                        index = x;
                        break;
                    } else if (result > 0 && sorted) {
                    	// we've passed the object, early out
                    	break;
                    }
                }
            } else {
                for (int x = 0; x < count; x++) {
                    if (contents[x] == object) {
                        index = x;
                        break;
                    } 
                }  
            }
        }
        return index;
    }
    
    /**
     * Sorts the array.  If the array is already sorted, no work will be performed unless
     * the forceResort parameter is set to true.  If a comparator has been specified with
     * setComparator(), it will be used for the sort; otherwise the object's natural ordering will
     * be used.
     * @param forceResort  If set to true, the array will be resorted even if the order of the
     * objects in the array has not changed since the last sort.
     */
    public void sort(boolean forceResort) {
        if (!mSorted || forceResort) {
           if (mComparator != null) {
               mSorter.sort(mContents, mCount, mComparator);
           } else {
               DebugLog.d("FixedSizeArray", "No comparator specified for this type, using Arrays.sort().");
               
               Arrays.sort(mContents, 0, mCount);
           }
           mSorted = true;
        }
    }
    
    /** Returns the number of objects in the array. */
    public int getCount() {
        return mCount;
    }
    
    /** Returns the maximum number of objects that can be inserted inot this array. */
    public int getCapacity() {
        return mContents.length;
    }
    
    /** Sets a comparator to use for sorting and searching. */
    public void setComparator(Comparator<T> comparator) {
        mComparator = comparator;
        mSorted = false;
    }
    
    public void setSorter(Sorter<T> sorter) {
        mSorter = sorter;
    }
}