📄 treelist.java
字号:
/*
* Copyright 2004-2006 The Apache Software Foundation
*
* 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 org.apache.commons.collections.list;
import java.util.AbstractList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import org.apache.commons.collections.OrderedIterator;
/**
* A <code>List</code> implementation that is optimised for fast insertions and
* removals at any index in the list.
* <p>
* This list implementation utilises a tree structure internally to ensure that
* all insertions and removals are O(log n). This provides much faster performance
* than both an <code>ArrayList</code> and a <code>LinkedList</code> where elements
* are inserted and removed repeatedly from anywhere in the list.
* <p>
* The following relative performance statistics are indicative of this class:
* <pre>
* get add insert iterate remove
* TreeList 3 5 1 2 1
* ArrayList 1 1 40 1 40
* LinkedList 5800 1 350 2 325
* </pre>
* <code>ArrayList</code> is a good general purpose list implementation.
* It is faster than <code>TreeList</code> for most operations except inserting
* and removing in the middle of the list. <code>ArrayList</code> also uses less
* memory as <code>TreeList</code> uses one object per entry.
* <p>
* <code>LinkedList</code> is rarely a good choice of implementation.
* <code>TreeList</code> is almost always a good replacement for it, although it
* does use sligtly more memory.
*
* @since Commons Collections 3.1
* @version $Revision: 370952 $ $Date: 2006-01-21 01:49:21 +0000 (Sat, 21 Jan 2006) $
*
* @author Joerg Schmuecker
* @author Stephen Colebourne
*/
public class TreeList extends AbstractList {
// add; toArray; iterator; insert; get; indexOf; remove
// TreeList = 1260;7360;3080; 160; 170;3400; 170;
// ArrayList = 220;1480;1760; 6870; 50;1540; 7200;
// LinkedList = 270;7360;3350;55860;290720;2910;55200;
/** The root node in the AVL tree */
private AVLNode root;
/** The current size of the list */
private int size;
//-----------------------------------------------------------------------
/**
* Constructs a new empty list.
*/
public TreeList() {
super();
}
/**
* Constructs a new empty list that copies the specified list.
*
* @param coll the collection to copy
* @throws NullPointerException if the collection is null
*/
public TreeList(Collection coll) {
super();
addAll(coll);
}
//-----------------------------------------------------------------------
/**
* Gets the element at the specified index.
*
* @param index the index to retrieve
* @return the element at the specified index
*/
public Object get(int index) {
checkInterval(index, 0, size() - 1);
return root.get(index).getValue();
}
/**
* Gets the current size of the list.
*
* @return the current size
*/
public int size() {
return size;
}
/**
* Gets an iterator over the list.
*
* @return an iterator over the list
*/
public Iterator iterator() {
// override to go 75% faster
return listIterator(0);
}
/**
* Gets a ListIterator over the list.
*
* @return the new iterator
*/
public ListIterator listIterator() {
// override to go 75% faster
return listIterator(0);
}
/**
* Gets a ListIterator over the list.
*
* @param fromIndex the index to start from
* @return the new iterator
*/
public ListIterator listIterator(int fromIndex) {
// override to go 75% faster
// cannot use EmptyIterator as iterator.add() must work
checkInterval(fromIndex, 0, size());
return new TreeListIterator(this, fromIndex);
}
/**
* Searches for the index of an object in the list.
*
* @return the index of the object, -1 if not found
*/
public int indexOf(Object object) {
// override to go 75% faster
if (root == null) {
return -1;
}
return root.indexOf(object, root.relativePosition);
}
/**
* Searches for the presence of an object in the list.
*
* @return true if the object is found
*/
public boolean contains(Object object) {
return (indexOf(object) >= 0);
}
/**
* Converts the list into an array.
*
* @return the list as an array
*/
public Object[] toArray() {
// override to go 20% faster
Object[] array = new Object[size()];
if (root != null) {
root.toArray(array, root.relativePosition);
}
return array;
}
//-----------------------------------------------------------------------
/**
* Adds a new element to the list.
*
* @param index the index to add before
* @param obj the element to add
*/
public void add(int index, Object obj) {
modCount++;
checkInterval(index, 0, size());
if (root == null) {
root = new AVLNode(index, obj, null, null);
} else {
root = root.insert(index, obj);
}
size++;
}
/**
* Sets the element at the specified index.
*
* @param index the index to set
* @param obj the object to store at the specified index
* @return the previous object at that index
* @throws IndexOutOfBoundsException if the index is invalid
*/
public Object set(int index, Object obj) {
checkInterval(index, 0, size() - 1);
AVLNode node = root.get(index);
Object result = node.value;
node.setValue(obj);
return result;
}
/**
* Removes the element at the specified index.
*
* @param index the index to remove
* @return the previous object at that index
*/
public Object remove(int index) {
modCount++;
checkInterval(index, 0, size() - 1);
Object result = get(index);
root = root.remove(index);
size--;
return result;
}
/**
* Clears the list, removing all entries.
*/
public void clear() {
modCount++;
root = null;
size = 0;
}
//-----------------------------------------------------------------------
/**
* Checks whether the index is valid.
*
* @param index the index to check
* @param startIndex the first allowed index
* @param endIndex the last allowed index
* @throws IndexOutOfBoundsException if the index is invalid
*/
private void checkInterval(int index, int startIndex, int endIndex) {
if (index < startIndex || index > endIndex) {
throw new IndexOutOfBoundsException("Invalid index:" + index + ", size=" + size());
}
}
//-----------------------------------------------------------------------
/**
* Implements an AVLNode which keeps the offset updated.
* <p>
* This node contains the real work.
* TreeList is just there to implement {@link java.util.List}.
* The nodes don't know the index of the object they are holding. They
* do know however their position relative to their parent node.
* This allows to calculate the index of a node while traversing the tree.
* <p>
* The Faedelung calculation stores a flag for both the left and right child
* to indicate if they are a child (false) or a link as in linked list (true).
*/
static class AVLNode {
/** The left child node or the predecessor if {@link #leftIsPrevious}.*/
private AVLNode left;
/** Flag indicating that left reference is not a subtree but the predecessor. */
private boolean leftIsPrevious;
/** The right child node or the successor if {@link #rightIsNext}. */
private AVLNode right;
/** Flag indicating that right reference is not a subtree but the successor. */
private boolean rightIsNext;
/** How many levels of left/right are below this one. */
private int height;
/** The relative position, root holds absolute position. */
private int relativePosition;
/** The stored element. */
private Object value;
/**
* Constructs a new node with a relative position.
*
* @param relativePosition the relative position of the node
* @param obj the value for the ndoe
* @param rightFollower the node with the value following this one
* @param leftFollower the node with the value leading this one
*/
private AVLNode(int relativePosition, Object obj, AVLNode rightFollower, AVLNode leftFollower) {
this.relativePosition = relativePosition;
value = obj;
rightIsNext = true;
leftIsPrevious = true;
right = rightFollower;
left = leftFollower;
}
/**
* Gets the value.
*
* @return the value of this node
*/
Object getValue() {
return value;
}
/**
* Sets the value.
*
* @param obj the value to store
*/
void setValue(Object obj) {
this.value = obj;
}
/**
* Locate the element with the given index relative to the
* offset of the parent of this node.
*/
AVLNode get(int index) {
int indexRelativeToMe = index - relativePosition;
if (indexRelativeToMe == 0) {
return this;
}
AVLNode nextNode = ((indexRelativeToMe < 0) ? getLeftSubTree() : getRightSubTree());
if (nextNode == null) {
return null;
}
return nextNode.get(indexRelativeToMe);
}
/**
* Locate the index that contains the specified object.
*/
int indexOf(Object object, int index) {
if (getLeftSubTree() != null) {
int result = left.indexOf(object, index + left.relativePosition);
if (result != -1) {
return result;
}
}
if (value == null ? value == object : value.equals(object)) {
return index;
}
if (getRightSubTree() != null) {
return right.indexOf(object, index + right.relativePosition);
}
return -1;
}
/**
* Stores the node and its children into the array specified.
*
* @param array the array to be filled
* @param index the index of this node
*/
void toArray(Object[] array, int index) {
array[index] = value;
if (getLeftSubTree() != null) {
left.toArray(array, index + left.relativePosition);
}
if (getRightSubTree() != null) {
right.toArray(array, index + right.relativePosition);
}
}
/**
* Gets the next node in the list after this one.
*
* @return the next node
*/
AVLNode next() {
if (rightIsNext || right == null) {
return right;
}
return right.min();
}
/**
* Gets the node in the list before this one.
*
* @return the previous node
*/
AVLNode previous() {
if (leftIsPrevious || left == null) {
return left;
}
return left.max();
}
/**
* Inserts a node at the position index.
*
* @param index is the index of the position relative to the position of
* the parent node.
* @param obj is the object to be stored in the position.
*/
AVLNode insert(int index, Object obj) {
int indexRelativeToMe = index - relativePosition;
if (indexRelativeToMe <= 0) {
return insertOnLeft(indexRelativeToMe, obj);
} else {
return insertOnRight(indexRelativeToMe, obj);
}
}
private AVLNode insertOnLeft(int indexRelativeToMe, Object obj) {
AVLNode ret = this;
if (getLeftSubTree() == null) {
setLeft(new AVLNode(-1, obj, this, left), null);
} else {
setLeft(left.insert(indexRelativeToMe, obj), null);
}
if (relativePosition >= 0) {
relativePosition++;
}
ret = balance();
recalcHeight();
return ret;
}
private AVLNode insertOnRight(int indexRelativeToMe, Object obj) {
AVLNode ret = this;
if (getRightSubTree() == null) {
setRight(new AVLNode(+1, obj, right, this), null);
} else {
setRight(right.insert(indexRelativeToMe, obj), null);
}
if (relativePosition < 0) {
relativePosition--;
}
ret = balance();
recalcHeight();
return ret;
}
//-----------------------------------------------------------------------
/**
* Gets the left node, returning null if its a faedelung.
*/
private AVLNode getLeftSubTree() {
return (leftIsPrevious ? null : left);
}
/**
* Gets the right node, returning null if its a faedelung.
*/
private AVLNode getRightSubTree() {
return (rightIsNext ? null : right);
}
/**
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -