All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.zoodb.jdo.internal.util.BucketTreeStack Maven / Gradle / Ivy

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