Initial commit
This adds the initial contents of the repository.
This commit is contained in:
7
.classpath
Normal file
7
.classpath
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
17
.project
Normal file
17
.project
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>DataStructures</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
11
.settings/org.eclipse.jdt.core.prefs
Normal file
11
.settings/org.eclipse.jdt.core.prefs
Normal file
@@ -0,0 +1,11 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.compiler.compliance=1.7
|
||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.source=1.7
|
||||
60
.settings/org.eclipse.jdt.ui.prefs
Normal file
60
.settings/org.eclipse.jdt.ui.prefs
Normal file
@@ -0,0 +1,60 @@
|
||||
eclipse.preferences.version=1
|
||||
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
|
||||
sp_cleanup.add_default_serial_version_id=true
|
||||
sp_cleanup.add_generated_serial_version_id=false
|
||||
sp_cleanup.add_missing_annotations=false
|
||||
sp_cleanup.add_missing_deprecated_annotations=true
|
||||
sp_cleanup.add_missing_methods=false
|
||||
sp_cleanup.add_missing_nls_tags=false
|
||||
sp_cleanup.add_missing_override_annotations=true
|
||||
sp_cleanup.add_missing_override_annotations_interface_methods=true
|
||||
sp_cleanup.add_serial_version_id=false
|
||||
sp_cleanup.always_use_blocks=true
|
||||
sp_cleanup.always_use_parentheses_in_expressions=false
|
||||
sp_cleanup.always_use_this_for_non_static_field_access=false
|
||||
sp_cleanup.always_use_this_for_non_static_method_access=false
|
||||
sp_cleanup.convert_functional_interfaces=false
|
||||
sp_cleanup.convert_to_enhanced_for_loop=false
|
||||
sp_cleanup.correct_indentation=false
|
||||
sp_cleanup.format_source_code=false
|
||||
sp_cleanup.format_source_code_changes_only=false
|
||||
sp_cleanup.insert_inferred_type_arguments=false
|
||||
sp_cleanup.make_local_variable_final=true
|
||||
sp_cleanup.make_parameters_final=false
|
||||
sp_cleanup.make_private_fields_final=true
|
||||
sp_cleanup.make_type_abstract_if_missing_method=false
|
||||
sp_cleanup.make_variable_declarations_final=false
|
||||
sp_cleanup.never_use_blocks=false
|
||||
sp_cleanup.never_use_parentheses_in_expressions=true
|
||||
sp_cleanup.on_save_use_additional_actions=true
|
||||
sp_cleanup.organize_imports=false
|
||||
sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
|
||||
sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
|
||||
sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
|
||||
sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
|
||||
sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
|
||||
sp_cleanup.remove_private_constructors=true
|
||||
sp_cleanup.remove_redundant_type_arguments=true
|
||||
sp_cleanup.remove_trailing_whitespaces=true
|
||||
sp_cleanup.remove_trailing_whitespaces_all=true
|
||||
sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
|
||||
sp_cleanup.remove_unnecessary_casts=false
|
||||
sp_cleanup.remove_unnecessary_nls_tags=false
|
||||
sp_cleanup.remove_unused_imports=false
|
||||
sp_cleanup.remove_unused_local_variables=false
|
||||
sp_cleanup.remove_unused_private_fields=true
|
||||
sp_cleanup.remove_unused_private_members=false
|
||||
sp_cleanup.remove_unused_private_methods=true
|
||||
sp_cleanup.remove_unused_private_types=true
|
||||
sp_cleanup.sort_members=false
|
||||
sp_cleanup.sort_members_all=false
|
||||
sp_cleanup.use_anonymous_class_creation=false
|
||||
sp_cleanup.use_blocks=false
|
||||
sp_cleanup.use_blocks_only_for_return_and_throw=false
|
||||
sp_cleanup.use_lambda=true
|
||||
sp_cleanup.use_parentheses_in_expressions=false
|
||||
sp_cleanup.use_this_for_non_static_field_access=false
|
||||
sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
|
||||
sp_cleanup.use_this_for_non_static_method_access=false
|
||||
sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
|
||||
sp_cleanup.use_type_arguments=false
|
||||
60
README.md
60
README.md
@@ -1,2 +1,60 @@
|
||||
# RedBlackNode
|
||||
`RedBlackNode` is a Java implementation of red-black trees. By subclassing `RedBlackNode`, clients can add arbitrary data and augmentation information to each node. (self-balancing binary search tree, self-balancing BST, augment, augmented)
|
||||
`RedBlackNode` is a Java implementation of red-black trees. By subclassing
|
||||
`RedBlackNode`, clients can add arbitrary data and augmentation information to
|
||||
each node. (self-balancing binary search tree, self-balancing BST, augment,
|
||||
augmented)
|
||||
|
||||
# Features
|
||||
* Supports min, max, root, predecessor, successor, insert, remove, rotate,
|
||||
split, concatenate, create balanced tree, and compare operations. The running
|
||||
time of each operation has optimal big O bounds.
|
||||
* Supports arbitrary augmentation by overriding `augment()`. Examples of
|
||||
augmentation are the number of non-leaf nodes in a subtree and the sum of the
|
||||
values in a subtree.
|
||||
* The parent and child links and the color are public fields. This gives
|
||||
clients flexibility. However, it is possible for a client to violate the
|
||||
red-black or BST properties.
|
||||
* "Assert is valid" methods allow clients to check for errors in the structure
|
||||
or contents of a red-black tree. This is useful for debugging.
|
||||
* As a bonus (a proof of concept and a test case), this includes the `TreeList`
|
||||
class, a `List` implementation backed by a red-black tree augmented by subtree
|
||||
size.
|
||||
* Tested in Java 6.0 and 7.0. It might also work in Java 5.0.
|
||||
|
||||
# Limitations:
|
||||
* Augmentations that depend on information stored in a node's ancestors are not
|
||||
(easily) supported. For example, augmenting each node with the number of
|
||||
nodes in the left subtree is not (easily and efficiently) supported, because
|
||||
in order to perform a right rotation, we would need to use the parent's
|
||||
augmentation information. However, `RedBlackNode` supports augmenting each
|
||||
node with the number of nodes in the subtree, which is basically equivalent.
|
||||
* The running time of each operation has optimal big O bounds. However, beyond
|
||||
this, no special effort has been made to optimize performance.
|
||||
|
||||
# Example
|
||||
<pre lang="java">
|
||||
/** Red-black tree augmented by the sum of the values in the subtree. */
|
||||
public class SumNode extends RedBlackNode<SumNode> {
|
||||
public int value;
|
||||
public int sum;
|
||||
|
||||
public SumNode(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean augment() {
|
||||
int newSum = value + left.sum + right.sum;
|
||||
if (newSum == sum) {
|
||||
return false;
|
||||
} else {
|
||||
sum = newSum;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
# Documentation
|
||||
For more detailed instructions, check the source code to see the full API and
|
||||
Javadoc documentation.
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.github.btrekkie.arbitrary_order_collection;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Provides objects ordered in an arbitrary, but consistent fashion, through the createValue() method. To determine the
|
||||
* relative order of two values, use ArbitraryOrderValue.compareTo. We may only compare values on which we have not
|
||||
* called "remove" that were created in the same ArbitraryOrderCollection instance. Note that despite the name,
|
||||
* ArbitraryOrderCollection does not implement Collection.
|
||||
*/
|
||||
/* We implement an ArbitraryOrderCollection using a red-black tree. We order the nodes arbitrarily.
|
||||
*/
|
||||
public class ArbitraryOrderCollection {
|
||||
/** The Comparator for ordering ArbitraryOrderNodes. */
|
||||
private static final Comparator<ArbitraryOrderNode> NODE_COMPARATOR = new Comparator<ArbitraryOrderNode>() {
|
||||
@Override
|
||||
public int compare(ArbitraryOrderNode node1, ArbitraryOrderNode node2) {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/** The root node of the tree. */
|
||||
private ArbitraryOrderNode root = new ArbitraryOrderNode();
|
||||
|
||||
/** Adds and returns a new value for ordering. */
|
||||
public ArbitraryOrderValue createValue() {
|
||||
ArbitraryOrderNode node = new ArbitraryOrderNode();
|
||||
root = root.insert(node, true, NODE_COMPARATOR);
|
||||
return new ArbitraryOrderValue(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified value from this collection. Assumes we obtained the value by calling createValue() on this
|
||||
* instance of ArbitraryOrderCollection. After calling "remove" on a value, we may no longer compare it to other
|
||||
* values.
|
||||
*/
|
||||
public void remove(ArbitraryOrderValue value) {
|
||||
root = value.node.remove();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.github.btrekkie.arbitrary_order_collection;
|
||||
|
||||
import com.github.btrekkie.red_black_node.RedBlackNode;
|
||||
|
||||
/** A node in an ArbitraryOrderCollection tree. See ArbitraryOrderCollection. */
|
||||
class ArbitraryOrderNode extends RedBlackNode<ArbitraryOrderNode> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.github.btrekkie.arbitrary_order_collection;
|
||||
|
||||
/**
|
||||
* A value in an ArbitraryOrderCollection. To determine the relative order of two values in the same collection, call
|
||||
* compareTo.
|
||||
*/
|
||||
public class ArbitraryOrderValue implements Comparable<ArbitraryOrderValue> {
|
||||
/** The node that establishes this value's relative position. */
|
||||
final ArbitraryOrderNode node;
|
||||
|
||||
ArbitraryOrderValue(ArbitraryOrderNode node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ArbitraryOrderValue other) {
|
||||
return node.compareTo(other.node);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.github.btrekkie.arbitrary_order_collection.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.github.btrekkie.arbitrary_order_collection.ArbitraryOrderCollection;
|
||||
import com.github.btrekkie.arbitrary_order_collection.ArbitraryOrderValue;
|
||||
|
||||
public class ArbitraryOrderCollectionTest {
|
||||
/** Tests ArbitraryOrderCollection. */
|
||||
@Test
|
||||
public void test() {
|
||||
ArbitraryOrderCollection collection = new ArbitraryOrderCollection();
|
||||
List<ArbitraryOrderValue> values1 = new ArrayList<ArbitraryOrderValue>(5);
|
||||
ArbitraryOrderValue value = collection.createValue();
|
||||
assertEquals(0, value.compareTo(value));
|
||||
values1.add(value);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
values1.add(collection.createValue());
|
||||
}
|
||||
Collections.sort(values1);
|
||||
List<ArbitraryOrderValue> values2 = new ArrayList<ArbitraryOrderValue>(10);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
value = collection.createValue();
|
||||
values2.add(value);
|
||||
}
|
||||
for (int i = 0; i < 5; i++) {
|
||||
collection.remove(values2.get(2 * i));
|
||||
}
|
||||
assertEquals(0, values1.get(0).compareTo(values1.get(0)));
|
||||
assertTrue(values1.get(0).compareTo(values1.get(1)) < 0);
|
||||
assertTrue(values1.get(1).compareTo(values1.get(0)) > 0);
|
||||
assertTrue(values1.get(4).compareTo(values1.get(2)) > 0);
|
||||
assertTrue(values1.get(0).compareTo(values1.get(4)) < 0);
|
||||
|
||||
collection = new ArbitraryOrderCollection();
|
||||
values1 = new ArrayList<ArbitraryOrderValue>(1000);
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
value = collection.createValue();
|
||||
values1.add(value);
|
||||
}
|
||||
for (int i = 0; i < 500; i++) {
|
||||
collection.remove(values1.get(2 * i));
|
||||
}
|
||||
values2 = new ArrayList<ArbitraryOrderValue>(500);
|
||||
for (int i = 0; i < 500; i++) {
|
||||
values2.add(values1.get(2 * i + 1));
|
||||
}
|
||||
for (int i = 0; i < 500; i++) {
|
||||
values2.get(0).compareTo(values2.get(i));
|
||||
}
|
||||
Collections.sort(values2);
|
||||
for (int i = 0; i < 500; i++) {
|
||||
collection.createValue();
|
||||
}
|
||||
for (int i = 0; i < 499; i++) {
|
||||
assertTrue(values2.get(i).compareTo(values2.get(i + 1)) < 0);
|
||||
assertTrue(values2.get(i + 1).compareTo(values2.get(i)) > 0);
|
||||
}
|
||||
for (int i = 1; i < 500; i++) {
|
||||
assertEquals(0, values2.get(i).compareTo(values2.get(i)));
|
||||
assertTrue(values2.get(0).compareTo(values2.get(i)) < 0);
|
||||
assertTrue(values2.get(i).compareTo(values2.get(0)) > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
60
src/com/github/btrekkie/interval_tree/IntervalTree.java
Normal file
60
src/com/github/btrekkie/interval_tree/IntervalTree.java
Normal file
@@ -0,0 +1,60 @@
|
||||
package com.github.btrekkie.interval_tree;
|
||||
|
||||
/**
|
||||
* An interval tree data structure, which supports adding or removing an interval and finding an arbitrary interval that
|
||||
* contains a specified value.
|
||||
*/
|
||||
/* The interval tree is ordered in ascending order of the start an interval, with ties broken by the end of the
|
||||
* interval. Each node is augmented with the maximum ending value of an interval in the subtree rooted at the node.
|
||||
*/
|
||||
public class IntervalTree {
|
||||
/** The root node of the tree. */
|
||||
private IntervalTreeNode root = IntervalTreeNode.LEAF;
|
||||
|
||||
/** Adds the specified interval to this. */
|
||||
public void addInterval(IntervalTreeInterval interval) {
|
||||
root = root.insert(new IntervalTreeNode(interval), true, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified interval from this, if it is present.
|
||||
* @param interval The interval.
|
||||
* @return Whether the interval was present.
|
||||
*/
|
||||
public boolean removeInterval(IntervalTreeInterval interval) {
|
||||
IntervalTreeNode node = root;
|
||||
while (!node.isLeaf()) {
|
||||
if (interval.start < node.interval.start) {
|
||||
node = node.left;
|
||||
} else if (interval.start > node.interval.start) {
|
||||
node = node.right;
|
||||
} else if (interval.end < node.interval.end) {
|
||||
node = node.left;
|
||||
} else if (interval.end > node.interval.end) {
|
||||
node = node.right;
|
||||
} else {
|
||||
root = node.remove();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an aribtrary IntervalTreeInterval in this that contains the specified value. Returns null if there is no
|
||||
* such interval.
|
||||
*/
|
||||
public IntervalTreeInterval findInterval(double value) {
|
||||
IntervalTreeNode node = root;
|
||||
while (!node.isLeaf()) {
|
||||
if (value >= node.interval.start && value <= node.interval.end) {
|
||||
return node.interval;
|
||||
} else if (value <= node.left.maxEnd) {
|
||||
node = node.left;
|
||||
} else {
|
||||
node = node.right;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.github.btrekkie.interval_tree;
|
||||
|
||||
/**
|
||||
* An inclusive range of values [start, end]. Two intervals are equal if they have the same starting and ending values.
|
||||
*/
|
||||
public class IntervalTreeInterval {
|
||||
/** The smallest value in the range. */
|
||||
public final double start;
|
||||
|
||||
/** The largest value in the range. */
|
||||
public final double end;
|
||||
|
||||
public IntervalTreeInterval(double start, double end) {
|
||||
if (start > end) {
|
||||
throw new IllegalArgumentException("The end of the range must be at most the start");
|
||||
}
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof IntervalTreeInterval)) {
|
||||
return false;
|
||||
}
|
||||
IntervalTreeInterval interval = (IntervalTreeInterval)obj;
|
||||
return start == interval.start && end == interval.end;
|
||||
}
|
||||
}
|
||||
68
src/com/github/btrekkie/interval_tree/IntervalTreeNode.java
Normal file
68
src/com/github/btrekkie/interval_tree/IntervalTreeNode.java
Normal file
@@ -0,0 +1,68 @@
|
||||
package com.github.btrekkie.interval_tree;
|
||||
|
||||
import com.github.btrekkie.red_black_node.RedBlackNode;
|
||||
|
||||
/**
|
||||
* A node in an IntervalTree. See the comments for the implementation of IntervalTree. Its compareTo method orders
|
||||
* nodes as suggested in the comments for the implementation of IntervalTree.
|
||||
*/
|
||||
class IntervalTreeNode extends RedBlackNode<IntervalTreeNode> {
|
||||
/** The dummy leaf node. */
|
||||
public static final IntervalTreeNode LEAF = new IntervalTreeNode();
|
||||
|
||||
/** The interval stored in this node. */
|
||||
public IntervalTreeInterval interval;
|
||||
|
||||
/** The maximum ending value of an interval in the subtree rooted at this node. */
|
||||
public double maxEnd;
|
||||
|
||||
public IntervalTreeNode(IntervalTreeInterval interval) {
|
||||
this.interval = interval;
|
||||
maxEnd = interval.end;
|
||||
}
|
||||
|
||||
/** Constructs a new dummy leaf node. */
|
||||
private IntervalTreeNode() {
|
||||
interval = null;
|
||||
maxEnd = Double.NEGATIVE_INFINITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean augment() {
|
||||
double newMaxEnd = Math.max(interval.end, Math.max(left.maxEnd, right.maxEnd));
|
||||
if (newMaxEnd == maxEnd) {
|
||||
return false;
|
||||
} else {
|
||||
maxEnd = newMaxEnd;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assertNodeIsValid() {
|
||||
double expectedMaxEnd;
|
||||
if (isLeaf()) {
|
||||
expectedMaxEnd = Double.NEGATIVE_INFINITY;
|
||||
} else {
|
||||
expectedMaxEnd = Math.max(interval.end, Math.max(left.maxEnd, right.maxEnd));
|
||||
}
|
||||
if (maxEnd != expectedMaxEnd) {
|
||||
throw new RuntimeException("The node's maxEnd does not match that of the children");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(IntervalTreeNode other) {
|
||||
if (interval.start != interval.end) {
|
||||
return Double.compare(interval.start, other.interval.start);
|
||||
} else {
|
||||
return Double.compare(interval.end, other.interval.end);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assertSubtreeIsValid() {
|
||||
super.assertSubtreeIsValid();
|
||||
assertOrderIsValid(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.github.btrekkie.interval_tree.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.github.btrekkie.interval_tree.IntervalTree;
|
||||
import com.github.btrekkie.interval_tree.IntervalTreeInterval;
|
||||
|
||||
public class IntervalTreeTest {
|
||||
/** Tests IntervalTree. */
|
||||
@Test
|
||||
public void test() {
|
||||
IntervalTree tree = new IntervalTree();
|
||||
assertNull(tree.findInterval(0.5));
|
||||
assertNull(tree.findInterval(-1));
|
||||
tree.addInterval(new IntervalTreeInterval(5, 7));
|
||||
tree.addInterval(new IntervalTreeInterval(42, 48));
|
||||
tree.addInterval(new IntervalTreeInterval(-1, 2));
|
||||
tree.addInterval(new IntervalTreeInterval(6, 12));
|
||||
tree.addInterval(new IntervalTreeInterval(21, 23));
|
||||
assertTrue(tree.removeInterval(new IntervalTreeInterval(-1, 2)));
|
||||
assertFalse(tree.removeInterval(new IntervalTreeInterval(-1, 2)));
|
||||
tree.addInterval(new IntervalTreeInterval(-6, -2));
|
||||
assertEquals(new IntervalTreeInterval(6, 12), tree.findInterval(8));
|
||||
assertNull(tree.findInterval(0));
|
||||
assertEquals(new IntervalTreeInterval(21, 23), tree.findInterval(21));
|
||||
assertEquals(new IntervalTreeInterval(42, 48), tree.findInterval(48));
|
||||
IntervalTreeInterval interval = tree.findInterval(6.5);
|
||||
assertTrue(new IntervalTreeInterval(5, 7).equals(interval) || new IntervalTreeInterval(6, 12).equals(interval));
|
||||
|
||||
tree = new IntervalTree();
|
||||
for (int i = 0; i < 500; i++) {
|
||||
tree.addInterval(new IntervalTreeInterval(2 * i, 2 * i + 1));
|
||||
}
|
||||
for (int i = 0; i < 250; i++) {
|
||||
tree.removeInterval(new IntervalTreeInterval(4 * i + 2, 4 * i + 3));
|
||||
}
|
||||
assertNull(tree.findInterval(123.5));
|
||||
assertEquals(new IntervalTreeInterval(124, 125), tree.findInterval(124.5));
|
||||
assertEquals(new IntervalTreeInterval(776, 777), tree.findInterval(776));
|
||||
assertEquals(new IntervalTreeInterval(0, 1), tree.findInterval(0.5));
|
||||
assertEquals(new IntervalTreeInterval(996, 997), tree.findInterval(997));
|
||||
}
|
||||
}
|
||||
1224
src/com/github/btrekkie/red_black_node/RedBlackNode.java
Normal file
1224
src/com/github/btrekkie/red_black_node/RedBlackNode.java
Normal file
File diff suppressed because it is too large
Load Diff
28
src/com/github/btrekkie/red_black_node/Reference.java
Normal file
28
src/com/github/btrekkie/red_black_node/Reference.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package com.github.btrekkie.red_black_node;
|
||||
|
||||
/**
|
||||
* Wraps a value using reference equality. In other words, two references are equal only if their values are the same
|
||||
* object instance, as in ==.
|
||||
* @param <T> The type of value.
|
||||
*/
|
||||
class Reference<T> {
|
||||
/** The value this wraps. */
|
||||
private final T value;
|
||||
|
||||
public Reference(T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof Reference)) {
|
||||
return false;
|
||||
}
|
||||
Reference<?> reference = (Reference<?>)obj;
|
||||
return value == reference.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return System.identityHashCode(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package com.github.btrekkie.red_black_node.test;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Tests RedBlackNode. Most of the testing for RedBlackNode takes place in TreeListTest, IntervalTreeTest, and
|
||||
* ArbitraryOrderCollectionTest, which test realistic use cases of RedBlackNode. TreeListTest tests most of the
|
||||
* RedBlackNode methods, while IntervalTreeTest tests non-structural augmentation and the "insert" method,
|
||||
* ArbitraryOrderCollectionTest tests compareTo, and RedBlackNodeTest tests assertSubtreeIsValid() and
|
||||
* assertOrderIsValid.
|
||||
*/
|
||||
public class RedBlackNodeTest {
|
||||
/**
|
||||
* Returns whether the subtree rooted at the specified node is valid, as in TestRedBlackNode.assertSubtreeIsValid().
|
||||
*/
|
||||
private boolean isSubtreeValid(TestRedBlackNode node) {
|
||||
try {
|
||||
node.assertSubtreeIsValid();
|
||||
return true;
|
||||
} catch (RuntimeException exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the nodes in the subtree rooted at the specified node are ordered correctly, as in
|
||||
* TestRedBlackNode.assertOrderIsValid.
|
||||
* @param comparator A comparator indicating how the nodes should be ordered. If this is null, we use the nodes'
|
||||
* natural ordering, as in TestRedBlackNode.compare.
|
||||
*/
|
||||
private boolean isOrderValid(TestRedBlackNode node, Comparator<TestRedBlackNode> comparator) {
|
||||
try {
|
||||
node.assertOrderIsValid(null);
|
||||
return true;
|
||||
} catch (RuntimeException exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests RedBlackNode.assertSubtreeIsValid() and RedBlackNode.assertOrderIsValid. */
|
||||
@Test
|
||||
public void testAssertIsValid() {
|
||||
// Create a perfectly balanced tree of height 3
|
||||
TestRedBlackNode node0 = new TestRedBlackNode(0);
|
||||
TestRedBlackNode node1 = new TestRedBlackNode(1);
|
||||
TestRedBlackNode node2 = new TestRedBlackNode(2);
|
||||
TestRedBlackNode node3 = new TestRedBlackNode(3);
|
||||
TestRedBlackNode node4 = new TestRedBlackNode(4);
|
||||
TestRedBlackNode node5 = new TestRedBlackNode(5);
|
||||
TestRedBlackNode node6 = new TestRedBlackNode(6);
|
||||
node0.parent = node1;
|
||||
node0.left = TestRedBlackNode.LEAF;
|
||||
node0.right = TestRedBlackNode.LEAF;
|
||||
node1.parent = node3;
|
||||
node1.left = node0;
|
||||
node1.right = node2;
|
||||
node1.isRed = true;
|
||||
node2.parent = node1;
|
||||
node2.left = TestRedBlackNode.LEAF;
|
||||
node2.right = TestRedBlackNode.LEAF;
|
||||
node3.left = node1;
|
||||
node3.right = node5;
|
||||
node4.parent = node5;
|
||||
node4.left = TestRedBlackNode.LEAF;
|
||||
node4.right = TestRedBlackNode.LEAF;
|
||||
node5.parent = node3;
|
||||
node5.left = node4;
|
||||
node5.right = node6;
|
||||
node5.isRed = true;
|
||||
node6.parent = node5;
|
||||
node6.left = TestRedBlackNode.LEAF;
|
||||
node6.right = TestRedBlackNode.LEAF;
|
||||
|
||||
node3.left = node3;
|
||||
node3.right = node3;
|
||||
node3.parent = node3;
|
||||
assertFalse(isSubtreeValid(node3));
|
||||
node3.left = node1;
|
||||
node3.right = node5;
|
||||
node3.parent = null;
|
||||
|
||||
node0.parent = node3;
|
||||
assertFalse(isSubtreeValid(node3));
|
||||
node0.parent = node1;
|
||||
|
||||
node1.right = node0;
|
||||
assertFalse(isSubtreeValid(node3));
|
||||
node1.right = node2;
|
||||
|
||||
node5.isRed = false;
|
||||
assertFalse(isSubtreeValid(node3));
|
||||
assertTrue(isSubtreeValid(node5));
|
||||
node5.isRed = true;
|
||||
|
||||
node3.isRed = true;
|
||||
assertFalse(isSubtreeValid(node3));
|
||||
assertTrue(isSubtreeValid(node5));
|
||||
node3.isRed = false;
|
||||
|
||||
node0.isRed = true;
|
||||
node2.isRed = true;
|
||||
node4.isRed = true;
|
||||
node6.isRed = true;
|
||||
assertFalse(isSubtreeValid(node3));
|
||||
node0.isRed = false;
|
||||
node2.isRed = false;
|
||||
node4.isRed = false;
|
||||
node6.isRed = false;
|
||||
|
||||
TestRedBlackNode.LEAF.isRed = true;
|
||||
assertFalse(isSubtreeValid(node3));
|
||||
TestRedBlackNode.LEAF.isRed = false;
|
||||
|
||||
TestRedBlackNode.LEAF.isValid = false;
|
||||
assertFalse(isSubtreeValid(node3));
|
||||
assertFalse(isSubtreeValid(TestRedBlackNode.LEAF));
|
||||
TestRedBlackNode.LEAF.isValid = true;
|
||||
|
||||
node1.isValid = false;
|
||||
assertFalse(isSubtreeValid(node3));
|
||||
node1.isValid = true;
|
||||
|
||||
node3.value = 2;
|
||||
node2.value = 3;
|
||||
assertFalse(isOrderValid(node3, null));
|
||||
assertFalse(
|
||||
isOrderValid(node3, new Comparator<TestRedBlackNode>() {
|
||||
@Override
|
||||
public int compare(TestRedBlackNode node1, TestRedBlackNode node2) {
|
||||
return node1.value - node2.value;
|
||||
}
|
||||
}));
|
||||
node3.value = 3;
|
||||
node2.value = 2;
|
||||
|
||||
node2.value = 4;
|
||||
node4.value = 2;
|
||||
assertFalse(isOrderValid(node3, null));
|
||||
node2.value = 2;
|
||||
node4.value = 4;
|
||||
|
||||
node0.value = 1;
|
||||
node1.value = 0;
|
||||
assertFalse(isOrderValid(node3, null));
|
||||
node0.value = 0;
|
||||
node1.value = 1;
|
||||
|
||||
// Do all of the assertions for which the tree is supposed to be valid at the end, to make sure we didn't make a
|
||||
// mistake undoing any of the modifications
|
||||
assertTrue(isSubtreeValid(node3));
|
||||
assertTrue(isSubtreeValid(node1));
|
||||
assertTrue(isSubtreeValid(node0));
|
||||
assertTrue(isSubtreeValid(TestRedBlackNode.LEAF));
|
||||
assertTrue(isOrderValid(node3, null));
|
||||
assertTrue(isOrderValid(node1, null));
|
||||
assertTrue(isOrderValid(node0, null));
|
||||
assertTrue(isOrderValid(TestRedBlackNode.LEAF, null));
|
||||
assertTrue(
|
||||
isOrderValid(node3, new Comparator<TestRedBlackNode>() {
|
||||
@Override
|
||||
public int compare(TestRedBlackNode node1, TestRedBlackNode node2) {
|
||||
return node1.value - node2.value;
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.github.btrekkie.red_black_node.test;
|
||||
|
||||
import com.github.btrekkie.red_black_node.RedBlackNode;
|
||||
|
||||
/** A RedBlackNode for RedBlackNodeTest. */
|
||||
class TestRedBlackNode extends RedBlackNode<TestRedBlackNode> {
|
||||
/** The dummy leaf node. */
|
||||
public static final TestRedBlackNode LEAF = new TestRedBlackNode();
|
||||
|
||||
/** The value stored in this node. "value" is unspecified if this is a leaf node. */
|
||||
public int value;
|
||||
|
||||
/** Whether this node is considered valid, as in assertNodeIsValid(). */
|
||||
public boolean isValid = true;
|
||||
|
||||
public TestRedBlackNode(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/** Constructs a new dummy leaf node. */
|
||||
private TestRedBlackNode() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assertNodeIsValid() {
|
||||
if (!isValid) {
|
||||
throw new RuntimeException("isValid is false");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(TestRedBlackNode other) {
|
||||
return value - other.value;
|
||||
}
|
||||
}
|
||||
461
src/com/github/btrekkie/tree_list/TreeList.java
Normal file
461
src/com/github/btrekkie/tree_list/TreeList.java
Normal file
@@ -0,0 +1,461 @@
|
||||
package com.github.btrekkie.tree_list;
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import com.github.btrekkie.red_black_node.RedBlackNode;
|
||||
|
||||
/**
|
||||
* Implements a list using a self-balancing binary search tree augmented by subtree size. The benefit of this compared
|
||||
* to ArrayList or LinkedList is that it supports both decent random access and quickly adding to or removing from the
|
||||
* middle of the list. Operations have the following running times:
|
||||
*
|
||||
* size(): O(1)
|
||||
* get, set, add, remove: O(log N)
|
||||
* addAll: O(log N + P), where P is the number of elements we add
|
||||
* iterator(): O(log N + P + M log N), where P is the number of elements over which we iterate and M is the number of
|
||||
* elements we remove
|
||||
* listIterator: O(log N + P + M log N + R log N), where P is the number of times we iterate over or set an element, M
|
||||
* is the number of elements we add or remove, and R is the number of times we change the direction of iteration
|
||||
* clear(): O(1), excluding garbage collection
|
||||
* subList.clear(): O(log N), excluding garbage collection
|
||||
* Constructor: O(N)
|
||||
*
|
||||
* This class is similar to an Apache Commons Collections class by the same name. I speculate that the Apache class is
|
||||
* faster than this (by a constant factor) for most operations. However, this class's implementations of addAll and
|
||||
* subList.clear() are asymptotically faster than the Apache class's implementations.
|
||||
*/
|
||||
public class TreeList<T> extends AbstractList<T> {
|
||||
/** The dummy leaf node. */
|
||||
private final TreeListNode<T> leaf = new TreeListNode<T>(null);
|
||||
|
||||
/** The root node of the tree. */
|
||||
private TreeListNode<T> root;
|
||||
|
||||
/** Constructs a new empty TreeList. */
|
||||
public TreeList() {
|
||||
root = leaf;
|
||||
}
|
||||
|
||||
/** Constructs a new TreeList containing the specified values, in iteration order. */
|
||||
public TreeList(Collection<? extends T> values) {
|
||||
root = createTree(values);
|
||||
}
|
||||
|
||||
/** Returns the root of a perfectly height-balanced tree containing the specified values, in iteration order. */
|
||||
private TreeListNode<T> createTree(Collection<? extends T> values) {
|
||||
List<TreeListNode<T>> nodes = new ArrayList<TreeListNode<T>>(values.size());
|
||||
for (T value : values) {
|
||||
nodes.add(new TreeListNode<T>(value));
|
||||
}
|
||||
return RedBlackNode.<TreeListNode<T>>createTree(nodes, leaf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node for get(index). Raises an IndexOutOfBoundsException if "index" is not in the range [0, size()).
|
||||
*/
|
||||
private TreeListNode<T> getNode(int index) {
|
||||
if (index < 0 || index >= root.size) {
|
||||
throw new IndexOutOfBoundsException("Index " + index + " is not in the range [0, " + root.size + ")");
|
||||
}
|
||||
int rank = index;
|
||||
TreeListNode<T> node = root;
|
||||
while (rank != node.left.size) {
|
||||
if (rank < node.left.size) {
|
||||
node = node.left;
|
||||
} else {
|
||||
rank -= node.left.size + 1;
|
||||
node = node.right;
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(int index) {
|
||||
return getNode(index).value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return root.size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return root == leaf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T set(int index, T value) {
|
||||
modCount++;
|
||||
TreeListNode<T> node = getNode(index);
|
||||
T oldValue = node.value;
|
||||
node.value = value;
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, T value) {
|
||||
if (index < 0 || index > root.size) {
|
||||
throw new IndexOutOfBoundsException("Index " + index + " is not in the range [0, " + root.size + "]");
|
||||
}
|
||||
modCount++;
|
||||
|
||||
TreeListNode<T> newNode = new TreeListNode<T>(value);
|
||||
newNode.left = leaf;
|
||||
newNode.right = leaf;
|
||||
if (root.isLeaf()) {
|
||||
root = newNode;
|
||||
newNode.isRed = false;
|
||||
return;
|
||||
}
|
||||
newNode.isRed = true;
|
||||
if (index < root.size) {
|
||||
TreeListNode<T> node = getNode(index);
|
||||
if (node.left.isLeaf()) {
|
||||
node.left = newNode;
|
||||
newNode.parent = node;
|
||||
} else {
|
||||
node = node.predecessor();
|
||||
node.right = newNode;
|
||||
newNode.parent = node;
|
||||
}
|
||||
} else {
|
||||
TreeListNode<T> node;
|
||||
node = root.max();
|
||||
node.right = newNode;
|
||||
newNode.parent = node;
|
||||
}
|
||||
|
||||
newNode.fixInsertion();
|
||||
while (root.parent != null) {
|
||||
root = root.parent;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T remove(int index) {
|
||||
TreeListNode<T> node = getNode(index);
|
||||
modCount++;
|
||||
T value = node.value;
|
||||
root = node.remove();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<? extends T> values) {
|
||||
if (index < 0 || index > root.size) {
|
||||
throw new IndexOutOfBoundsException("Index " + index + " is not in the range [0, " + root.size + "]");
|
||||
}
|
||||
modCount++;
|
||||
if (values.isEmpty()) {
|
||||
return false;
|
||||
} else {
|
||||
if (index >= root.size) {
|
||||
root = root.concatenate(createTree(values));
|
||||
} else {
|
||||
TreeListNode<T>[] split = root.split(getNode(index));
|
||||
root = split[0].concatenate(createTree(values)).concatenate(split[1]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends T> values) {
|
||||
modCount++;
|
||||
if (values.isEmpty()) {
|
||||
return false;
|
||||
} else {
|
||||
root = root.concatenate(createTree(values));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeRange(int startIndex, int endIndex) {
|
||||
if (startIndex != endIndex) {
|
||||
modCount++;
|
||||
TreeListNode<T> last;
|
||||
if (endIndex == root.size) {
|
||||
last = leaf;
|
||||
} else {
|
||||
TreeListNode<T>[] split = root.split(getNode(endIndex));
|
||||
root = split[0];
|
||||
last = split[1];
|
||||
}
|
||||
TreeListNode<T> first = root.split(getNode(startIndex))[0];
|
||||
root = first.concatenate(last);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
modCount++;
|
||||
root = leaf;
|
||||
}
|
||||
|
||||
/** The class for TreeList.iterator(). */
|
||||
private class TreeListIterator implements Iterator<T> {
|
||||
/** The value of TreeList.this.modCount we require to continue iteration without concurrent modification. */
|
||||
private int modCount = TreeList.this.modCount;
|
||||
|
||||
/**
|
||||
* The node containing the last element next() returned. This is null if we have yet to call next() or we have
|
||||
* called remove() since the last call to next().
|
||||
*/
|
||||
private TreeListNode<T> node;
|
||||
|
||||
/** The node containing next(). This is null if we have reached the end of the list. */
|
||||
private TreeListNode<T> nextNode;
|
||||
|
||||
/** Whether we have (successfully) called next(). */
|
||||
private boolean haveCalledNext;
|
||||
|
||||
private TreeListIterator() {
|
||||
if (root.isLeaf()) {
|
||||
nextNode = null;
|
||||
} else {
|
||||
nextNode = root.min();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return nextNode != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
if (nextNode == null) {
|
||||
throw new NoSuchElementException("Reached the end of the list");
|
||||
} else if (TreeList.this.modCount != modCount) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
haveCalledNext = true;
|
||||
node = nextNode;
|
||||
nextNode = nextNode.successor();
|
||||
return node.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
if (node == null) {
|
||||
if (!haveCalledNext) {
|
||||
throw new IllegalStateException("Must call next() before calling remove()");
|
||||
} else {
|
||||
throw new IllegalStateException("Already removed this element");
|
||||
}
|
||||
} else if (TreeList.this.modCount != modCount) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
root = node.remove();
|
||||
node = null;
|
||||
TreeList.this.modCount++;
|
||||
modCount = TreeList.this.modCount;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return new TreeListIterator();
|
||||
}
|
||||
|
||||
/** The class for TreeList.listIterator. */
|
||||
private class TreeListListIterator implements ListIterator<T> {
|
||||
/** The value of TreeList.this.modCount we require to continue iteration without concurrent modification. */
|
||||
private int modCount = TreeList.this.modCount;
|
||||
|
||||
/** The current return value for nextIndex(). */
|
||||
private int nextIndex;
|
||||
|
||||
/** The node for next(), or null if hasNext() is false. */
|
||||
private TreeListNode<T> nextNode;
|
||||
|
||||
/** The node for previous(), or null if hasPrevious() is false. */
|
||||
private TreeListNode<T> prevNode;
|
||||
|
||||
/** Whether we have called next() or previous(). */
|
||||
private boolean haveCalledNextOrPrevious;
|
||||
|
||||
/** Whether we (successfully) called next() more recently than previous(). */
|
||||
private boolean justCalledNext;
|
||||
|
||||
/**
|
||||
* Whether we have (successfully) called remove() or "add" since the last (successful) call to next() or
|
||||
* previous().
|
||||
*/
|
||||
private boolean haveModified;
|
||||
|
||||
/**
|
||||
* Constructs a new TreeListListIterator.
|
||||
* @param index The starting index, as in the "index" argument to listIterator. This method assumes that
|
||||
* 0 <= index < root.size.
|
||||
*/
|
||||
private TreeListListIterator(int index) {
|
||||
nextIndex = index;
|
||||
if (index > 0) {
|
||||
prevNode = getNode(index - 1);
|
||||
nextNode = prevNode.successor();
|
||||
} else {
|
||||
prevNode = null;
|
||||
if (root.size > 0) {
|
||||
nextNode = root.min();
|
||||
} else {
|
||||
nextNode = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
if (modCount != TreeList.this.modCount) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
return nextNode != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
if (nextNode == null) {
|
||||
throw new NoSuchElementException("Reached the end of the list");
|
||||
} else if (modCount != TreeList.this.modCount) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
haveCalledNextOrPrevious = true;
|
||||
justCalledNext = true;
|
||||
haveModified = false;
|
||||
nextIndex++;
|
||||
prevNode = nextNode;
|
||||
nextNode = nextNode.successor();
|
||||
return prevNode.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextIndex() {
|
||||
if (modCount != TreeList.this.modCount) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
return nextIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPrevious() {
|
||||
if (modCount != TreeList.this.modCount) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
return prevNode != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T previous() {
|
||||
if (prevNode == null) {
|
||||
throw new NoSuchElementException("Reached the beginning of the list");
|
||||
} else if (modCount != TreeList.this.modCount) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
haveCalledNextOrPrevious = true;
|
||||
justCalledNext = false;
|
||||
haveModified = false;
|
||||
nextIndex--;
|
||||
nextNode = prevNode;
|
||||
prevNode = prevNode.predecessor();
|
||||
return nextNode.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int previousIndex() {
|
||||
return nextIndex - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(T value) {
|
||||
if (!haveCalledNextOrPrevious) {
|
||||
throw new IllegalStateException("Must call next() or previous() before calling \"set\"");
|
||||
} else if (haveModified) {
|
||||
throw new IllegalStateException("Already modified the list at this position");
|
||||
} else if (modCount != TreeList.this.modCount) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
if (justCalledNext) {
|
||||
prevNode.value = value;
|
||||
} else {
|
||||
nextNode.value = value;
|
||||
}
|
||||
TreeList.this.modCount++;
|
||||
modCount = TreeList.this.modCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(T value) {
|
||||
if (haveModified) {
|
||||
throw new IllegalStateException("Already modified the list at this position");
|
||||
} else if (modCount != TreeList.this.modCount) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
|
||||
// Create the new node
|
||||
TreeListNode<T> newNode = new TreeListNode<T>(value);;
|
||||
newNode.left = leaf;
|
||||
newNode.right = leaf;
|
||||
newNode.isRed = true;
|
||||
|
||||
// Insert newNode. There is guaranteed to be a leaf child of prevNode or nextNode where we can insert it.
|
||||
if (nextNode != null && nextNode.left.isLeaf()) {
|
||||
nextNode.left = newNode;
|
||||
newNode.parent = nextNode;
|
||||
} else if (prevNode != null) {
|
||||
prevNode.right = newNode;
|
||||
newNode.parent = prevNode;
|
||||
} else {
|
||||
root = newNode;
|
||||
}
|
||||
|
||||
prevNode = newNode;
|
||||
newNode.fixInsertion();
|
||||
nextIndex++;
|
||||
haveModified = true;
|
||||
TreeList.this.modCount++;
|
||||
modCount = TreeList.this.modCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
if (!haveCalledNextOrPrevious) {
|
||||
throw new IllegalStateException("Must call next() or previous() before calling remove()");
|
||||
} else if (haveModified) {
|
||||
throw new IllegalStateException("Already modified the list at this position");
|
||||
} else if (modCount != TreeList.this.modCount) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
|
||||
if (justCalledNext) {
|
||||
TreeListNode<T> predecessor = prevNode.predecessor();
|
||||
root = prevNode.remove();
|
||||
prevNode = predecessor;
|
||||
} else {
|
||||
TreeListNode<T> successor = nextNode.successor();
|
||||
root = nextNode.remove();
|
||||
nextNode = successor;
|
||||
}
|
||||
|
||||
haveModified = true;
|
||||
TreeList.this.modCount++;
|
||||
modCount = TreeList.this.modCount;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<T> listIterator(int index) {
|
||||
if (index < 0 || index > root.size) {
|
||||
throw new IndexOutOfBoundsException("Index " + index + " is not in the range [0, " + root.size + "]");
|
||||
}
|
||||
return new TreeListListIterator(index);
|
||||
}
|
||||
}
|
||||
40
src/com/github/btrekkie/tree_list/TreeListNode.java
Normal file
40
src/com/github/btrekkie/tree_list/TreeListNode.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package com.github.btrekkie.tree_list;
|
||||
|
||||
import com.github.btrekkie.red_black_node.RedBlackNode;
|
||||
|
||||
/** A node in a TreeList. See the comments for TreeList. */
|
||||
class TreeListNode<T> extends RedBlackNode<TreeListNode<T>> {
|
||||
/** The element stored in the node. The value is unspecified if this is a leaf node. */
|
||||
public T value;
|
||||
|
||||
/** The number of elements in the subtree rooted at this node, not counting leaf nodes, as in TreeList.leaf. */
|
||||
public int size;
|
||||
|
||||
public TreeListNode(T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean augment() {
|
||||
int newSize = left.size + right.size + 1;
|
||||
if (newSize == size) {
|
||||
return false;
|
||||
} else {
|
||||
size = newSize;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assertNodeIsValid() {
|
||||
int expectedSize;
|
||||
if (isLeaf()) {
|
||||
expectedSize = 0;
|
||||
} else {
|
||||
expectedSize = left.size + right.size + 1;
|
||||
}
|
||||
if (size != expectedSize) {
|
||||
throw new RuntimeException("The node's size does not match that of the children");
|
||||
}
|
||||
}
|
||||
}
|
||||
551
src/com/github/btrekkie/tree_list/test/TreeListTest.java
Normal file
551
src/com/github/btrekkie/tree_list/test/TreeListTest.java
Normal file
@@ -0,0 +1,551 @@
|
||||
package com.github.btrekkie.tree_list.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.github.btrekkie.tree_list.TreeList;
|
||||
|
||||
public class TreeListTest {
|
||||
/** Tests TreeList.add. */
|
||||
@Test
|
||||
public void testAdd() {
|
||||
List<Integer> list = new TreeList<Integer>();
|
||||
assertEquals(Collections.emptyList(), list);
|
||||
assertEquals(0, list.size());
|
||||
assertTrue(list.isEmpty());
|
||||
|
||||
list.add(4);
|
||||
list.add(17);
|
||||
list.add(-4);
|
||||
list.add(null);
|
||||
assertEquals(Arrays.asList(4, 17, -4, null), list);
|
||||
assertEquals(4, list.size());
|
||||
assertFalse(list.isEmpty());
|
||||
assertEquals(-4, list.get(2).intValue());
|
||||
|
||||
boolean threwException;
|
||||
try {
|
||||
list.get(-3);
|
||||
threwException = false;
|
||||
} catch (IndexOutOfBoundsException exception) {
|
||||
threwException = true;
|
||||
}
|
||||
assertTrue(threwException);
|
||||
try {
|
||||
list.get(9);
|
||||
threwException = false;
|
||||
} catch (IndexOutOfBoundsException exception) {
|
||||
threwException = true;
|
||||
}
|
||||
assertTrue(threwException);
|
||||
try {
|
||||
list.add(-3, 5);
|
||||
threwException = false;
|
||||
} catch (IndexOutOfBoundsException exception) {
|
||||
threwException = true;
|
||||
}
|
||||
assertTrue(threwException);
|
||||
try {
|
||||
list.add(9, 10);
|
||||
threwException = false;
|
||||
} catch (IndexOutOfBoundsException exception) {
|
||||
threwException = true;
|
||||
}
|
||||
assertTrue(threwException);
|
||||
|
||||
list.add(1, 6);
|
||||
list.add(0, -1);
|
||||
list.add(6, 42);
|
||||
assertEquals(Arrays.asList(-1, 4, 6, 17, -4, null, 42), list);
|
||||
assertEquals(7, list.size());
|
||||
assertEquals(6, list.get(2).intValue());
|
||||
assertEquals(null, list.get(5));
|
||||
|
||||
list = new TreeList<Integer>();
|
||||
for (int i = 0; i < 200; i++) {
|
||||
list.add(i + 300);
|
||||
}
|
||||
for (int i = 0; i < 300; i++) {
|
||||
list.add(i, i);
|
||||
}
|
||||
for (int i = 499; i >= 0; i--) {
|
||||
list.add(500, i + 500);
|
||||
}
|
||||
List<Integer> expected = new ArrayList<Integer>(1000);
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
expected.add(i);
|
||||
}
|
||||
assertEquals(expected, list);
|
||||
assertEquals(1000, list.size());
|
||||
assertFalse(list.isEmpty());
|
||||
assertEquals(777, list.get(777).intValue());
|
||||
assertEquals(123, list.get(123).intValue());
|
||||
assertEquals(0, list.get(0).intValue());
|
||||
assertEquals(999, list.get(999).intValue());
|
||||
}
|
||||
|
||||
/** Tests TreeList.remove. */
|
||||
@Test
|
||||
public void testRemove() {
|
||||
List<Integer> list = new TreeList<Integer>();
|
||||
list.add(17);
|
||||
list.add(-5);
|
||||
list.add(null);
|
||||
list.add(0);
|
||||
list.add(1, 16);
|
||||
assertEquals(null, list.remove(3));
|
||||
assertEquals(17, list.remove(0).intValue());
|
||||
assertEquals(0, list.remove(2).intValue());
|
||||
assertEquals(Arrays.asList(16, -5), list);
|
||||
assertEquals(2, list.size());
|
||||
assertFalse(list.isEmpty());
|
||||
assertEquals(16, list.get(0).intValue());
|
||||
assertEquals(-5, list.get(1).intValue());
|
||||
|
||||
boolean threwException;
|
||||
try {
|
||||
list.remove(-3);
|
||||
threwException = false;
|
||||
} catch (IndexOutOfBoundsException exception) {
|
||||
threwException = true;
|
||||
}
|
||||
assertTrue(threwException);
|
||||
try {
|
||||
list.remove(9);
|
||||
threwException = false;
|
||||
} catch (IndexOutOfBoundsException exception) {
|
||||
threwException = true;
|
||||
}
|
||||
assertTrue(threwException);
|
||||
|
||||
list = new TreeList<Integer>();
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
list.add(i);
|
||||
}
|
||||
for (int i = 0; i < 250; i++) {
|
||||
assertEquals(2 * i + 501, list.remove(501).intValue());
|
||||
assertEquals(2 * i + 1, list.remove(i + 1).intValue());
|
||||
}
|
||||
List<Integer> expected = new ArrayList<Integer>(500);
|
||||
for (int i = 0; i < 500; i++) {
|
||||
expected.add(2 * i);
|
||||
}
|
||||
assertEquals(expected, list);
|
||||
assertEquals(500, list.size());
|
||||
assertFalse(list.isEmpty());
|
||||
assertEquals(0, list.get(0).intValue());
|
||||
assertEquals(998, list.get(499).intValue());
|
||||
assertEquals(84, list.get(42).intValue());
|
||||
assertEquals(602, list.get(301).intValue());
|
||||
}
|
||||
|
||||
/** Tests TreeList.set.*/
|
||||
@Test
|
||||
public void testSet() {
|
||||
List<Integer> list = new TreeList<Integer>();
|
||||
list.addAll(Arrays.asList(5, 17, 42, -6, null, 3, null));
|
||||
list.set(3, 12);
|
||||
list.set(0, 6);
|
||||
list.set(6, 88);
|
||||
boolean threwException;
|
||||
try {
|
||||
list.set(7, 2);
|
||||
threwException = false;
|
||||
} catch (IndexOutOfBoundsException exception) {
|
||||
threwException = true;
|
||||
}
|
||||
assertTrue(threwException);
|
||||
try {
|
||||
list.set(-1, 4);
|
||||
threwException = false;
|
||||
} catch (IndexOutOfBoundsException exception) {
|
||||
threwException = true;
|
||||
}
|
||||
assertTrue(threwException);
|
||||
assertEquals(Arrays.asList(6, 17, 42, 12, null, 3, 88), list);
|
||||
assertEquals(7, list.size());
|
||||
assertFalse(list.isEmpty());
|
||||
assertEquals(42, list.get(2).intValue());
|
||||
assertEquals(6, list.get(0).intValue());
|
||||
assertEquals(88, list.get(6).intValue());
|
||||
|
||||
list = new TreeList<Integer>();
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
list.add(999 - i);
|
||||
}
|
||||
for (int i = 0; i < 500; i++) {
|
||||
list.set(i, i);
|
||||
list.set(i + 500, i + 500);
|
||||
}
|
||||
List<Integer> expected = new ArrayList<Integer>(1000);
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
expected.add(i);
|
||||
}
|
||||
assertEquals(expected, list);
|
||||
assertEquals(1000, list.size());
|
||||
assertFalse(list.isEmpty());
|
||||
assertEquals(123, list.get(123).intValue());
|
||||
assertEquals(777, list.get(777).intValue());
|
||||
assertEquals(0, list.get(0).intValue());
|
||||
assertEquals(999, list.get(999).intValue());
|
||||
}
|
||||
|
||||
/** Tests TreeList.addAll. */
|
||||
@Test
|
||||
public void testAddAll() {
|
||||
List<Integer> list = new TreeList<Integer>();
|
||||
list.add(3);
|
||||
list.add(42);
|
||||
list.add(16);
|
||||
list.addAll(0, Arrays.asList(15, 4, -1));
|
||||
list.addAll(2, Arrays.asList(6, 14));
|
||||
list.addAll(1, Collections.<Integer>emptyList());
|
||||
list.addAll(8, Arrays.asList(null, 7));
|
||||
list.addAll(Arrays.asList(-5, 5));
|
||||
assertEquals(Arrays.asList(15, 4, 6, 14, -1, 3, 42, 16, null, 7, -5, 5), list);
|
||||
assertEquals(12, list.size());
|
||||
assertFalse(list.isEmpty());
|
||||
assertEquals(14, list.get(3).intValue());
|
||||
assertEquals(15, list.get(0).intValue());
|
||||
assertEquals(5, list.get(11).intValue());
|
||||
|
||||
list = new TreeList<Integer>();
|
||||
List<Integer> list2 = new ArrayList<Integer>(400);
|
||||
for (int i = 0; i < 200; i++) {
|
||||
list2.add(i + 100);
|
||||
}
|
||||
for (int i = 0; i < 200; i++) {
|
||||
list2.add(i + 700);
|
||||
}
|
||||
list.addAll(list2);
|
||||
list2.clear();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
list2.add(i);
|
||||
}
|
||||
list.addAll(0, list2);
|
||||
list2.clear();
|
||||
for (int i = 0; i < 400; i++) {
|
||||
list2.add(i + 300);
|
||||
}
|
||||
list.addAll(300, list2);
|
||||
list2.clear();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
list2.add(i + 900);
|
||||
}
|
||||
list.addAll(900, list2);
|
||||
List<Integer> expected = new ArrayList<Integer>(1000);
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
expected.add(i);
|
||||
}
|
||||
assertEquals(expected, list);
|
||||
assertEquals(1000, list.size());
|
||||
assertFalse(list.isEmpty());
|
||||
assertEquals(123, list.get(123).intValue());
|
||||
assertEquals(777, list.get(777).intValue());
|
||||
assertEquals(0, list.get(0).intValue());
|
||||
assertEquals(999, list.get(999).intValue());
|
||||
}
|
||||
|
||||
/** Tests TreeList.subList.clear(). */
|
||||
@Test
|
||||
public void testClearSubList() {
|
||||
List<Integer> list = new TreeList<Integer>();
|
||||
list.addAll(Arrays.asList(6, 42, -3, 15, 7, 99, 6, 12));
|
||||
list.subList(2, 4).clear();
|
||||
list.subList(0, 1).clear();
|
||||
list.subList(4, 4).clear();
|
||||
list.subList(3, 5).clear();
|
||||
assertEquals(Arrays.asList(42, 7, 99), list);
|
||||
assertEquals(3, list.size());
|
||||
assertFalse(list.isEmpty());
|
||||
assertEquals(42, list.get(0).intValue());
|
||||
assertEquals(7, list.get(1).intValue());
|
||||
assertEquals(99, list.get(2).intValue());
|
||||
|
||||
list = new TreeList<Integer>();
|
||||
for (int i = 0; i < 200; i++) {
|
||||
list.add(-1);
|
||||
}
|
||||
for (int i = 0; i < 500; i++) {
|
||||
list.add(i);
|
||||
}
|
||||
for (int i = 0; i < 400; i++) {
|
||||
list.add(-1);
|
||||
}
|
||||
for (int i = 0; i < 500; i++) {
|
||||
list.add(i + 500);
|
||||
}
|
||||
for (int i = 0; i < 600; i++) {
|
||||
list.add(-1);
|
||||
}
|
||||
list.subList(1600, 2200).clear();
|
||||
list.subList(777, 777).clear();
|
||||
list.subList(700, 1100).clear();
|
||||
list.subList(0, 200).clear();
|
||||
List<Integer> expected = new ArrayList<Integer>(1000);
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
expected.add(i);
|
||||
}
|
||||
assertEquals(expected, list);
|
||||
assertEquals(1000, list.size());
|
||||
assertFalse(list.isEmpty());
|
||||
assertEquals(123, list.get(123).intValue());
|
||||
assertEquals(777, list.get(777).intValue());
|
||||
assertEquals(0, list.get(0).intValue());
|
||||
assertEquals(999, list.get(999).intValue());
|
||||
|
||||
list = new TreeList<Integer>();
|
||||
list.addAll(Arrays.asList(-3, null, 8, 14, 9, 42));
|
||||
list.subList(0, 6).clear();
|
||||
assertEquals(Collections.emptyList(), list);
|
||||
assertEquals(0, list.size());
|
||||
assertTrue(list.isEmpty());
|
||||
}
|
||||
|
||||
/** Tests TreeList(Collection). */
|
||||
@Test
|
||||
public void testConstructor() {
|
||||
List<Integer> list = new TreeList<Integer>(Arrays.asList(1, 5, 42, -6, null, 3));
|
||||
assertEquals(Arrays.asList(1, 5, 42, -6, null, 3), list);
|
||||
assertEquals(6, list.size());
|
||||
assertFalse(list.isEmpty());
|
||||
assertEquals(42, list.get(2).intValue());
|
||||
assertEquals(1, list.get(0).intValue());
|
||||
assertEquals(3, list.get(5).intValue());
|
||||
|
||||
List<Integer> expected = new ArrayList<Integer>(1000);
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
expected.add(i);
|
||||
}
|
||||
list = new TreeList<Integer>(expected);
|
||||
assertEquals(expected, list);
|
||||
assertEquals(1000, list.size());
|
||||
assertFalse(list.isEmpty());
|
||||
assertEquals(123, list.get(123).intValue());
|
||||
assertEquals(777, list.get(777).intValue());
|
||||
assertEquals(0, list.get(0).intValue());
|
||||
assertEquals(999, list.get(999).intValue());
|
||||
}
|
||||
|
||||
/** Tests TreeList.clear(). */
|
||||
@Test
|
||||
public void testClear() {
|
||||
List<Integer> list = new TreeList<Integer>();
|
||||
list.addAll(Arrays.asList(7, 16, 5, 42));
|
||||
list.clear();
|
||||
assertEquals(Collections.emptyList(), list);
|
||||
assertEquals(0, list.size());
|
||||
assertTrue(list.isEmpty());
|
||||
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
list.add(i);
|
||||
}
|
||||
list.clear();
|
||||
assertEquals(Collections.emptyList(), list);
|
||||
assertEquals(0, list.size());
|
||||
assertTrue(list.isEmpty());
|
||||
}
|
||||
|
||||
/** Tests TreeList.iterator(). */
|
||||
@Test
|
||||
public void testIterator() {
|
||||
List<Integer> list = new TreeList<Integer>();
|
||||
Iterator<Integer> iterator = list.iterator();
|
||||
assertFalse(iterator.hasNext());
|
||||
boolean threwException;
|
||||
try {
|
||||
iterator.next();
|
||||
threwException = false;
|
||||
} catch (NoSuchElementException exception) {
|
||||
threwException = true;
|
||||
}
|
||||
assertTrue(threwException);
|
||||
|
||||
list = new TreeList<Integer>(Arrays.asList(42, 16, null, 7, 8, 3, 12));
|
||||
iterator = list.iterator();
|
||||
try {
|
||||
iterator.remove();
|
||||
threwException = false;
|
||||
} catch (IllegalStateException exception) {
|
||||
threwException = true;
|
||||
}
|
||||
assertTrue(threwException);
|
||||
assertTrue(iterator.hasNext());
|
||||
assertEquals(42, iterator.next().intValue());
|
||||
assertEquals(16, iterator.next().intValue());
|
||||
assertEquals(null, iterator.next());
|
||||
assertTrue(iterator.hasNext());
|
||||
assertEquals(7, iterator.next().intValue());
|
||||
iterator.remove();
|
||||
try {
|
||||
iterator.remove();
|
||||
threwException = false;
|
||||
} catch (IllegalStateException exception) {
|
||||
threwException = true;
|
||||
}
|
||||
assertTrue(threwException);
|
||||
assertTrue(iterator.hasNext());
|
||||
assertTrue(iterator.hasNext());
|
||||
assertEquals(8, iterator.next().intValue());
|
||||
assertTrue(iterator.hasNext());
|
||||
assertEquals(3, iterator.next().intValue());
|
||||
assertTrue(iterator.hasNext());
|
||||
assertEquals(12, iterator.next().intValue());
|
||||
assertFalse(iterator.hasNext());
|
||||
iterator.remove();
|
||||
assertFalse(iterator.hasNext());
|
||||
try {
|
||||
iterator.next();
|
||||
threwException = false;
|
||||
} catch (NoSuchElementException exception) {
|
||||
threwException = true;
|
||||
}
|
||||
assertTrue(threwException);
|
||||
assertEquals(Arrays.asList(42, 16, null, 8, 3), list);
|
||||
|
||||
list = new TreeList<Integer>();
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
list.add(i);
|
||||
}
|
||||
iterator = list.iterator();
|
||||
for (int i = 0; i < 500; i++) {
|
||||
assertTrue(iterator.hasNext());
|
||||
assertEquals(2 * i, iterator.next().intValue());
|
||||
assertTrue(iterator.hasNext());
|
||||
assertEquals(2 * i + 1, iterator.next().intValue());
|
||||
iterator.remove();
|
||||
}
|
||||
assertFalse(iterator.hasNext());
|
||||
List<Integer> expected = new ArrayList<Integer>(500);
|
||||
for (int i = 0; i < 500; i++) {
|
||||
expected.add(2 * i);
|
||||
}
|
||||
assertEquals(expected, list);
|
||||
}
|
||||
|
||||
/** Tests TreeList.listIterator. */
|
||||
@Test
|
||||
public void testListIterator() {
|
||||
List<Integer> list = new TreeList<Integer>();
|
||||
list.addAll(Arrays.asList(7, 16, 42, -3, 12, 25, 8, 9));
|
||||
boolean threwException;
|
||||
try {
|
||||
list.listIterator(-1);
|
||||
threwException = false;
|
||||
} catch (IndexOutOfBoundsException exception) {
|
||||
threwException = true;
|
||||
}
|
||||
assertTrue(threwException);
|
||||
try {
|
||||
list.listIterator(18);
|
||||
threwException = false;
|
||||
} catch (IndexOutOfBoundsException exception) {
|
||||
threwException = true;
|
||||
}
|
||||
assertTrue(threwException);
|
||||
ListIterator<Integer> iterator = list.listIterator(1);
|
||||
assertTrue(iterator.hasNext());
|
||||
assertTrue(iterator.hasPrevious());
|
||||
assertEquals(16, iterator.next().intValue());
|
||||
iterator.add(6);
|
||||
try {
|
||||
iterator.set(-1);
|
||||
threwException = false;
|
||||
} catch (IllegalStateException exception) {
|
||||
threwException = true;
|
||||
}
|
||||
assertTrue(threwException);
|
||||
try {
|
||||
iterator.add(9);
|
||||
threwException = false;
|
||||
} catch (IllegalStateException exception) {
|
||||
threwException = true;
|
||||
}
|
||||
assertTrue(threwException);
|
||||
try {
|
||||
iterator.remove();
|
||||
threwException = false;
|
||||
} catch (IllegalStateException exception) {
|
||||
threwException = true;
|
||||
}
|
||||
assertTrue(threwException);
|
||||
assertEquals(6, iterator.previous().intValue());
|
||||
assertEquals(6, iterator.next().intValue());
|
||||
assertEquals(2, iterator.previousIndex());
|
||||
assertEquals(3, iterator.nextIndex());
|
||||
assertEquals(42, iterator.next().intValue());
|
||||
assertEquals(-3, iterator.next().intValue());
|
||||
assertEquals(12, iterator.next().intValue());
|
||||
iterator.remove();
|
||||
assertEquals(-3, iterator.previous().intValue());
|
||||
iterator.remove();
|
||||
assertEquals(25, iterator.next().intValue());
|
||||
iterator.set(14);
|
||||
assertEquals(8, iterator.next().intValue());
|
||||
assertEquals(9, iterator.next().intValue());
|
||||
assertFalse(iterator.hasNext());
|
||||
try {
|
||||
iterator.next();
|
||||
threwException = false;
|
||||
} catch (NoSuchElementException exception) {
|
||||
threwException = true;
|
||||
}
|
||||
assertTrue(threwException);
|
||||
assertEquals(9, iterator.previous().intValue());
|
||||
iterator.set(10);
|
||||
assertEquals(Arrays.asList(7, 16, 6, 42, 14, 8, 10), list);
|
||||
assertEquals(7, list.size());
|
||||
assertFalse(list.isEmpty());
|
||||
assertEquals(42, list.get(3).intValue());
|
||||
assertEquals(7, list.get(0).intValue());
|
||||
assertEquals(10, list.get(6).intValue());
|
||||
|
||||
list = new TreeList<Integer>();
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
list.add(-1);
|
||||
}
|
||||
iterator = list.listIterator();
|
||||
for (int i = 0; i < 500; i++) {
|
||||
iterator.add(i);
|
||||
iterator.next();
|
||||
}
|
||||
for (int i = 0; i < 500; i++) {
|
||||
iterator.previous();
|
||||
iterator.remove();
|
||||
iterator.previous();
|
||||
}
|
||||
iterator = list.listIterator(500);
|
||||
for (int i = 0; i < 250; i++) {
|
||||
iterator.next();
|
||||
iterator.set(2 * i + 500);
|
||||
iterator.next();
|
||||
}
|
||||
for (int i = 0; i < 250; i++) {
|
||||
iterator.previous();
|
||||
iterator.set(999 - 2 * i);
|
||||
iterator.previous();
|
||||
}
|
||||
List<Integer> expected = new ArrayList<Integer>(1000);
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
expected.add(i);
|
||||
}
|
||||
assertEquals(expected, list);
|
||||
assertEquals(1000, list.size());
|
||||
assertFalse(list.isEmpty());
|
||||
assertEquals(123, list.get(123).intValue());
|
||||
assertEquals(777, list.get(777).intValue());
|
||||
assertEquals(0, list.get(0).intValue());
|
||||
assertEquals(999, list.get(999).intValue());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user