Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package com.cloudhopper.commons.util;
/*
* #%L
* ch-commons-util
* %%
* Copyright (C) 2012 Cloudhopper by Twitter
* %%
* 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.
* #L%
*/
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Utility class for maintaining a circular index of a fixed capacity. Useful
* for implementing a "queue" using a fixed-length array where the head and tail
* keep looping around the fixed capacity.
*
* @author joelauer (twitter: @jjlauer or http://twitter.com/jjlauer)
*/
public class CircularIndex {
private static Logger logger = LoggerFactory.getLogger(CircularIndex.class);
// should we reset the sequence if size = 0 (automatically resetting)
private boolean resetOnZero;
// fixed capacity of queue - index range of 0 to (capacity-1)
private long capacity;
// current size of queue - makes calculations easier
private long size;
// first item in queue
private long first;
// points to the next available space
private long next;
/**
* Creates a circular index based on a fixed capacity of elements. This
* capacity should represent the actual length of an array, etc. The max
* size of elements permitted in the index is exactly one less than the
* capacity (maxSize = capacity - 1). This max size is driven by the
* requirement that 1 empty space exists to keep track of where the first
* and last elements are in the index.
* @param capacity The fixed capacity of elements. If this index is to be
* used with an array, the capacity should match the capacity of the
* array. Must be > 0.
*/
public CircularIndex(long capacity) {
this(capacity, 0, 0);
}
public CircularIndex(long capacity, boolean resetOnZero) {
this(capacity, 0, 0, resetOnZero);
}
public CircularIndex(long capacity, long first, long size) {
this(capacity, first, size, false);
}
/**
* Creates a circular index based on a fixed capacity of elements. This
* capacity should represent the actual length of an array, etc. The max
* size of elements permitted in the index is exactly one less than the
* capacity (maxSize = capacity - 1). This max size is driven by the
* requirement that 1 empty space exists to keep track of where the first
* and last elements are in the index.
* @param capacity The fixed capacity of elements. If this index is to be
* used with an array, the capacity should match the capacity of the
* array. Must be > 0.
* @param first The initial index of the first element. This must be within
* the range based on the capacity.
* @param size The number of elements contained within the index. Used in
* conjunction with the first element index to calculate the "next"
* index used internally. Can be set to zero in order to control
* which will be the index of the first element added.
* @param resetOnZero Controls whether this index should automatically reset
* when the size hits zero. A reset sets the first and next indexes
* back to their defaults.
*/
public CircularIndex(long capacity, long first, long size, boolean resetOnZero) {
// verify capacity is valid
if (capacity <= 0) {
throw new IllegalArgumentException("Index capacity must be > 0");
}
this.capacity = capacity;
// verify the "first" parameter
if (first < 0) {
throw new IllegalArgumentException("Index first element must be >= 0 [first=" + first + "]");
} else if (first >= capacity) {
throw new IllegalArgumentException("Index first element must be < capacity [first=" + first + ", capacity=" + capacity + "]");
}
this.first = first;
// verify the "size" parameter
if (size < 0) {
throw new IllegalArgumentException("Index size element must be >= 0 [size=" + size + "]");
} else if (size > getMaxSize()) {
throw new IllegalArgumentException("Index size element must be < getMaxSize [size=" + size + ", getMaxSize=" + getMaxSize() + "]");
}
this.size = size;
// calculate "next" based on size
this.next = this.calculateNewIndex(first, size);
this.resetOnZero = resetOnZero;
checkReset();
}
/**
* Resets index back to defaults with a zero size and no elements. The
* first index will start from zero. The capacity remains the same as when
* the index was constructed.
*/
public void reset() {
this.size = 0;
this.first = 0;
this.next = 0;
}
private void checkReset() {
if (this.resetOnZero && this.size == 0) {
reset();
}
}
/**
* Gets the underlying capacity of the index. NOTE: This does not equal
* the maximum number of elements the index can support. The capacity is
* always 1 size larger than the maximum size since one empty space is used
* to track the first and last items in the index.
* @return The underlying capacity of the index.
*/
public long getCapacity() {
return this.capacity;
}
/**
* Gets the maximum number of elements this index can support. Always
* one less than the set capacity.
* @return The maximum number of elements this index can support.
*/
public long getMaxSize() {
// max size is always 1 less than capacity
return (this.capacity - 1);
}
/**
* Gets the current number of elements in this index.
* @return The number of elements in this index.
*/
public long getSize() {
return this.size;
}
/**
* Checks if the index is currently empty (contains no elements).
* @return True if the index is empty, otherwise false.
*/
public boolean isEmpty() {
return (first == next);
}
/**
* Checks if the index is currently full (contains all elements).
* @return True if the index is full, otherwise false.
*/
public boolean isFull() {
// would one more item would make next == first
return ((next + 1) % capacity == first);
}
protected long calculateNewIndex(long index, long offset) {
// capacity is added on LHS to handle negative offsets
// Java does not handle negative modulous operands as I thought, so
// this is a workaround to correctly calculate it
return (index + capacity + offset) % capacity;
}
/**
* Adds an element to the back of the index if its not full.
* @return True if the element was added or false if the index was full.
*/
public boolean addLast() {
// check if queue is full
if (isFull()) {
return false;
}
// increment next available space, rollover to beginning if needed
next = calculateNewIndex(next, 1);
//next = (next + 1) % capacity;
// always increment size
size++;
return true;
}
/**
* Adds an element to the front of the index if its not full.
* @return True if the element was added or false if the index was full.
*/
public boolean addFirst() {
// check if queue is full
if (isFull()) {
return false;
}
// increment next available space, rollover to beginning if needed
first = calculateNewIndex(first, -1);
//first = (first - 1) % capacity;
// always increment size
size++;
return true;
}
/**
* Removes the last element from the back of the index if its not empty.
* @return True if the element was removed or false if the index was empty.
*/
public boolean removeLast() {
// check if queue is empty
if (isEmpty()) {
return false;
}
// increment first item, rollver to beginning if needed
next = calculateNewIndex(next, -1);
//next = (next - 1) % capacity;
// always decrement size
size--;
// check if we should reset
checkReset();
return true;
}
/**
* Removes the first element from the front of the index if its not empty.
* @return True if the element was removed or false if the index was empty.
*/
public boolean removeFirst() {
// check if queue is empty
if (isEmpty()) {
return false;
}
// increment first item, rollver to beginning if needed
first = calculateNewIndex(first, 1);
//first = (first + 1) % capacity;
// always decrement size
size--;
// check if we should reset
checkReset();
return true;
}
/**
* Gets the index to the first element if it exists.
* @return The index of the first element (head) or -1 if no elements exist.
*/
public long getFirst() {
if (isEmpty()) {
return -1;
}
return this.first;
}
/**
* Gets the index to the last element if it exists.
* @return The index of the last element (tail) or -1 if no elements exist.
*/
public long getLast() {
if (isEmpty()) {
return -1;
}
// next points to current free space, so the one directly before it
// would actually be the last item
// NOTE: -1 mod 4 = 3 (isn't the modulus operator schweet?)
//return ((next - 1) % capacity);
return calculateNewIndex(next, -1);
}
/**
* Gets the index where the next last element will be put. This is the index
* where the next "addLast" operation would put the element. Will return -1 if
* the index is full (i.e. no next space available).
* @return The index of where the next last element will be put or -1 if the
* the index is full.
*/
public long getNextLast() {
if (isFull()) {
return -1;
} else if (isEmpty()) {
// interesting case, always the same as the first
return first;
} else {
return next;
}
}
/**
* Gets the index where the next first element will be put. This is the index
* where the next "addFirst" operation would put the element. Will return -1 if
* the index is full (i.e. no next space available).
* @return The index of where the next first element will be put or -1 if the
* the index is full.
*/
public long getNextFirst() {
if (isFull()) {
return -1;
} else if (isEmpty()) {
// interesting case, always the current first
return first;
} else {
return calculateNewIndex(first, -1);
}
}
/** protected methods just for testing purposes **/
protected long getInternalFirst() {
return this.first;
}
protected long getInternalNext() {
return this.next;
}
@Override
public String toString() {
return new StringBuilder(50)
.append("[size=")
.append(size)
.append(", first=")
.append(first)
.append(", next=")
.append(next)
.append("]")
.toString();
}
}