
org.zoodb.jdo.internal.util.BucketTreeStack Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of parent Show documentation
Show all versions of parent Show documentation
ZooDB Java JDO Object Database.
The newest version!
/*
* Copyright 2009-2013 Tilmann Zaeschke. All rights reserved.
*
* This file is part of ZooDB.
*
* ZooDB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ZooDB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ZooDB. If not, see .
*
* See the README and COPYING files for further information.
*/
package org.zoodb.jdo.internal.util;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.RandomAccess;
/**
* Bucket array list with fast random access by index. It has concurrency potential.
*
* See PerfIterator for performance considerations.
*
* Works like an ArrayList but is much faster when getting big, because the bucket architecture
* avoids copying the existing list to a new array as happens in the ArrayList.
*
* Features:
* - Fast insert, traversal and access by index.
* - Some potential as CONCURRENT collection, because an existing tree is only changed by adding
* elements to the end or by inserting a higher order root. But an existing section of the tree
* never changes (except clean() and remove(), see remove()), so iterators are inherently stable.
*
* Limitations:
* - Only the last element can be removed!
* - Elements can only be added to the end.
*
* @author Tilmann Zaeschke
*/
public class BucketTreeStack
implements RandomAccess, java.io.Serializable, Iterable
{
private static final long serialVersionUID = 8683452581122892189L;
private transient Object[] bucket;
/**
* The size of the BucketList (the number of elements it contains).
*/
private int size;
private final int bucketExp;
private final int bucketSize;
private int bucketDepth;
private int modCount = 0;
/**
* Constructs an empty list with an initial capacity of ten.
*/
public BucketTreeStack() {
this((byte) 10); //1024
}
/**
* Constructs an empty list with an initial capacity of ten.
* @param bucketExponent List buckets will contain 2^bucketExponent elements.
*/
public BucketTreeStack(byte bucketExponent) {
this.bucketExp = bucketExponent;
this.bucketSize = 1 << bucketExponent;
bucket = new Object[bucketSize];
}
/**
* Trims the capacity of this BucketArrayList instance to be the
* list's current size. An application can use this operation to minimize
* the storage of an BucketArrayList instance.
*/
public void trimToSize() {
modCount++;
while ((bucketDepth > 0) && (bucketSize << ((bucketDepth-1)*bucketExp) >= size)) {
bucket = (Object[]) bucket[0];
}
}
/**
* Increases the capacity of this BucketArrayList instance, if
* necessary, to ensure that it can hold at least the number of elements
* specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
public void ensureCapacity(int minCapacity) {
modCount++;
while (bucketSize << (bucketDepth*bucketExp) < minCapacity) {
Object oldData[] = bucket;
bucket = new Object[bucketSize];
bucket[0] = oldData;
bucketDepth++;
}
}
/**
* Returns the number of elements in this list.
*
* @return the number of elements in this list
*/
public int size() {
return size;
}
/**
* Returns true if this list contains no elements.
*
* @return true if this list contains no elements
*/
public boolean isEmpty() {
return size == 0;
}
// Positional Access Operations
/**
* Returns the element at the specified position in this list.
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException
*/
public E get(int index) {
rangeCheck(index);
return getElement(bucket, bucketDepth, index);
}
@SuppressWarnings("unchecked")
private E getElement(Object[] parent, int depth, int index) {
if (depth > 0) {
int pos = index >> (depth*bucketExp);
int index2 = ((1 << (depth*bucketExp))-1) & index;
return getElement((Object[]) parent[pos], depth-1, index2);
}
return (E) parent[index];
}
/**
* Replaces the element at the specified position in this list with
* the specified element.
*
* @param index index of the element to replace
* @param e element to be stored at the specified position
* @return the element previously at the specified position
* @throws IndexOutOfBoundsException
*/
public E set(int index, E e) {
rangeCheck(index);
return addElement(bucket, bucketDepth, index, e);
}
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return true (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacity(size + 1); // Increments modCount!!
addElement(bucket, bucketDepth, size++, e);
return true;
}
@SuppressWarnings("unchecked")
private E addElement(Object[] parent, int depth, int index, E e) {
if (depth > 0) {
int pos = index >> (depth*bucketExp);
int index2 = ((1 << (depth*bucketExp))-1) & index;
if (parent[pos] == null) {
parent[pos] = new Object[bucketSize];
}
return addElement((Object[]) parent[pos], depth-1, index2, e);
}
E old = (E) parent[index];
parent[index] = e;
return old;
}
/**
* Removes the element at the specified position in this list.
* Only the last element in the list can be removed.
*
* @return the element that was removed from the list
* @throws IndexOutOfBoundsException
*/
public E removeLast() {
modCount++;
//TODO clean up reference?
//If we don't clean it up, we get a (small?) memory leak.
//If we clean it up, the iterator may not be stable anymore.
E obj = get(size - 1);
size--;
return obj;
// rangeCheck(index);
//
// modCount++;
//
// if (index == size - 1) {
// E obj = get(index);
// size--;
// return obj;
// }
// throw new UnsupportedOperationException();
}
/**
* Removes all of the elements from this list. The list will
* be empty after this call returns.
*/
public void clear() {
modCount++;
// this does not allocate more memory:
//TODO possibly faster: create new empty bucket? -> test speeds create vs set-to-null
for (int i = 0; i < bucketSize; i++) {
bucket[i] = null;
}
bucketDepth = 0;
size = 0;
}
/**
* Checks if the given index is in range. If not, throws an appropriate
* runtime exception. This method does *not* check if the index is
* negative: It is always used immediately prior to an array access,
* which throws an IndexOutOfBoundsException if index is negative.
*/
private void rangeCheck(int index) {
if (index >= size) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
}
@Override
public Iterator iterator() {
return new BucketIterator(size - 1);
}
/**
* Iterator for BucketArrayList.
* Concurrency:
* - If clear() and remove() are not used, concurrency is simple, we just remember the current
* max and iterator to it. This is effectively a COW iterator. If clear() and remove() are
* used, we would have to make sure that the references are not deleted. For example:
* remove() keeps references; only clear() removes the whole tree (replace by new empty
* bucket); but the iterator could keep a reference to the tree as long as required.
* - We could make the iterator adaptive, so it only iterates to the end of the current list.
* This works nicely with clear() and remove(), but results in a somewhat unusual concurrency
* policy.
* TODO choose and implement.
*
* TODO
* Other improvement: may it be faster to store a stack here and keep a reference to the
* current bucket? Iteration should be faster for large pages, but Iterator creation is slower.
* But for large pages, using get to find them should be fast as well.
*/
private class BucketIterator implements Iterator {
private int pos = 0;
private final int max;
/**
* @param max Maximum index = (size-1)
*/
private BucketIterator(int max) {
this.max = max;
}
@Override
public boolean hasNext() {
return pos <= max;
}
@Override
public E next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return get(pos++);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy