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
|
/* Glazed Lists (c) 2003-2006 */
/* http://publicobject.com/glazedlists/ publicobject.com,*/
/* O'Dell Engineering Ltd.*/
package ca.odell.glazedlists.impl.adt;
// For Lists and Iterators
import java.util.AbstractList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* A SparseList is an ADT to complement the CompressableList and IndexedTree
* ADTs. IndexedTree provides accessible nodes that can recalculate their index
* on the fly so users can avoid dealing with index offsetting. CompressableList
* provides list compression capabilites that allow a list to be accessed
* by both the real index and a compressed index. The compressed index
* corresponds to the index of the current value as though no nulls exist
* in the list. While this is a powerful feature, the larger benefits of the
* compression of nulls are a significant performance boost and smaller footprint
* for lists that tend towards containing a significant number of nulls.
*
* <p>The SparseList was created to provide the indexed accessible nodes
* as found in IndexedTree while reaping the performance and memory enhancements
* of CompressableList. These optimizations have been taken several steps further
* to gain significantly better performance over the current implementation of
* CompressableList.
*
* <p>In an effort to maximize performance, this ADT does NOT validate that
* arguments passed to methods are valid in any way. While this adds inherent
* risk to the use of this code, this is a volatile implementation class. As
* such, it should only be used for internal GlazedList development. It is up
* to the calling code to do any argument validation which may be necessary. If
* you are still concerned, consider the benefits. Being in a tree structure
* means the methods on this ADT are often recursive. Recursively validating
* arguments makes no sense, and has a real-world impact on performance, while
* not a Big-Oh impact.
*
* <p>Every effort has been made to squeeze the highest performance and smallest
* footprint out of this data structure. These benefits hopefully don't come at
* the cost of code clarity or maintainability. The memory usage of this ADT
* is bound to the number of non-null elements. Null elements have no additional
* memory impact on the data structure.
*
* <p>The intent of this high-performance, low-cost data structure is for
* improving the scalability of some of the GlazedLists. It is technically
* possible to scale this ADT above the Integer.MAX_SIZE barrier imposed by
* integer-based indexing. However, doing so requires particular care in the
* structuring of the list and should be avoided if possible. It is advised
* that users do their best to operate within the bounds of the Integer.MAX_SIZE
* size limit.
*
* @author <a href="mailto:kevin@swank.ca">Kevin Maltby</a>
*
*/
public final class SparseList extends AbstractList {
/** the root of the tree */
private SparseListNode root = null;
/** the total size of this data structure */
private int size = 0;
/** the size of tree */
private int treeSize = 0;
/**
* Gets the size of this {@link List}.
*/
@Override
public int size() {
return size;
}
/**
* Inserts a value into this tree at the given index
*/
@Override
public void add(int index, Object value) {
// Let nulls be inserted by the method created for that purpose
if(value == null) {
addNulls(index, 1);
// The tree already has a root
} else if(root != null) {
// Insert a real node into the trailing nulls
if(index >= treeSize) {
int movingNulls = index - treeSize;
size -= movingNulls;
// Insert at the end of the tree
root.insertAtEnd(value, movingNulls);
treeSizeChanged();
// Insert into the tree
} else {
root.insert(index, value);
treeSizeChanged();
}
// Create a root for this tree
} else {
root = new SparseListNode(this, null, value, index);
treeSize = index + 1;
size++;
}
}
/**
* Inserts a sequence of nulls into the tree
*/
public void addNulls(int index, int length) {
// Increase the total tree size
size += length;
// The nulls are to be added to the actual tree
if(root != null && index < treeSize) {
root.insertEmptySpace(index, length);
treeSize += length;
}
}
/**
* Gets the value in this tree at the given index
*/
@Override
public Object get(int index) {
SparseListNode node = getNode(index);
// The value at that index is a null
if(node == null) return null;
return node.getValue();
}
/**
* Gets the node for an element in this tree at the given index
* or null if the value at that index is null
*/
public SparseListNode getNode(int index) {
if(root != null && index < treeSize) return root.getNode(index);
return null;
}
/**
* Sets the value of the node at the given index
*/
@Override
public Object set(int index, Object value) {
// The set occurs in the actual tree
if(root != null && index < treeSize) {
Object returnValue = root.set(index, value);
// This is not intutive as the tree size should 'never' change on a
// set call. However, since sets can result in inserts and deletes
// this is currently necessary. But I'm working on it.
treeSizeChanged();
return returnValue;
// The set occurs in the trailing nulls
} else {
// no-op to set a null to a null
if(value == null) {
return null;
// this is equivalent to adding at this index
} else {
size--;
add(index, value);
return null;
}
}
}
/**
* Removes the nodex at the given index
*/
@Override
public Object remove(int index) {
// The remove occurs in the actual tree
if(root != null && index < treeSize) {
Object returnValue = root.remove(index);
treeSizeChanged();
return returnValue;
// The remove occurs in the trailing nulls
} else {
size--;
return null;
}
}
/**
* Clears the tree
*/
@Override
public void clear() {
size = 0;
root = null;
}
/**
* Sets the root for this tree. This method is exposed for the
* SparseListNode in the event that the tree's root is involved in
* an AVL rotation.
*/
void setRootNode(SparseListNode root) {
this.root = root;
if(root == null) {
size -= treeSize;
treeSize = 0;
}
}
/**
* Notifies the tree that the underlying tree size has changed. This method
* is exposed for SparseListNode to make size adjustments.
*/
void treeSizeChanged() {
size -= treeSize;
treeSize = root != null ? root.size() : 0;
size += treeSize;
}
/**
* Obtains an {@link Iterator} for this {@link List}.
*/
@Override
public Iterator iterator() {
if(size == 0) return Collections.EMPTY_LIST.iterator();
return new SparseListNode.SparseListIterator(this, root);
}
/**
* Prints out the structure of the tree for debug purposes.
*/
public void printDebug() {
System.out.println(root.toString());
}
}
|