(See the next iteration.)
I have subclassed java.util.ArrayList
in order to be able to "rotate" it in constant time simply by moving a finger index. See what I have:
RotableList.java
package net.coderodde.util;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Objects;
import java.util.Spliterator;
/**
* This class implements a rotable list. Pushing to the front or the end of this
* list runs in constant amortized time. Rotation runs in constant time.
*
* @author Rodion "rodde" Efremov
* @version 1.6 (Mar 24, 2016)
*/
public class RotableList<E> extends ArrayList<E> {
private int finger;
@Override
public E get(int index) {
checkAccessIndex(index);
return super.get((index + finger) % size());
}
@Override
public E set(int index, E element) {
checkAccessIndex(index);
E ret = get(index);
super.set((index + finger) % size(), element);
return ret;
}
@Override
public void add(int index, E element) {
checkAdditionIndex(index);
super.add((index + finger) % (size() + 1), element);
}
@Override
public boolean add(E element) {
add(size(), element);
return true;
}
@Override
public E remove(int index) {
checkRemovalIndex(index);
E ret = get(index);
super.remove((index + finger) % size());
return ret;
}
@Override
public int indexOf(Object o) {
int size = size();
for (int index = 0; index < size; ++index) {
if (Objects.equals(o, get(index))) {
return index;
}
}
return -1;
}
@Override
public int lastIndexOf(Object o) {
for (int index = size() - 1; index >= 0; --index) {
if (Objects.equals(o, get(index))) {
return index;
}
}
return -1;
}
@Override
public void sort(Comparator<? super E> c) {
super.sort(c);
finger = 0;
}
@Override
public Iterator<E> iterator() {
throw new UnsupportedOperationException();
}
@Override
public ListIterator<E> listIterator() {
throw new UnsupportedOperationException();
}
@Override
public ListIterator<E> listIterator(int index) {
throw new UnsupportedOperationException();
}
@Override
public Spliterator<E> spliterator() {
throw new UnsupportedOperationException();
}
@Override
public List<E> subList(int fromIndex, int toIndex) {
throw new UnsupportedOperationException();
}
@Override
public Object[] toArray() {
throw new UnsupportedOperationException();
}
@Override
public <E> E[] toArray(E[] a) {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object o) {
int size = size();
for (int index = 0; index < size; ++index) {
if (Objects.equals(o, get(index))) {
remove(index);
return true;
}
}
return false;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("[");
int size = size();
for (int index = 0; index < size; ++index) {
sb.append(get(index));
if (index < size - 1) {
sb.append(", ");
}
}
return sb.append("]").toString();
}
public void rotate(int offset) {
finger -= offset;
finger %= size();
if (finger < 0) {
finger += size();
}
}
private void checkAccessIndex(int index) {
if (index < 0) {
throw new IndexOutOfBoundsException(
"The access index is negative: " + index + ".");
}
if (index >= size()) {
throw new IndexOutOfBoundsException(
"The access index is too large: " + index + "." +
"The size of the list is " + size() + ".");
}
}
private void checkAdditionIndex(int index) {
if (index < 0) {
throw new IndexOutOfBoundsException(
"The access index is negative: " + index + ".");
}
if (index > size()) {
throw new IndexOutOfBoundsException(
"The addition index is too large: " + index + "." +
"The size of the list is " + size() + ".");
}
}
private void checkRemovalIndex(int index) {
if (index < 0) {
throw new IndexOutOfBoundsException(
"The removal index is negative: " + index + ".");
}
if (index >= size()) {
throw new IndexOutOfBoundsException(
"The removal index is too large: " + index + "." +
"The size of the list is " + size() + ".");
}
}
public static void main(String[] args) {
RotableList<Integer> list = new RotableList<>();
for (int i = 0; i < 5; ++i) {
list.add(i);
}
System.out.println("Rotating to the right:");
for (int i = 0; i < list.size(); ++i) {
System.out.println(list);
list.rotate(1);
}
System.out.println("Rotating to the left:");
for (int i = 0; i < list.size(); ++i) {
System.out.println(list);
list.rotate(-1);
}
}
}
RotableListTest.java
package net.coderodde.util;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.Before;
public class RotableListTest {
private final RotableList<Integer> list = new RotableList<>();
@Before
public void before() {
list.clear();
}
private void load(int n) {
for (int i = 0; i < n; ++i) {
list.add(i);
}
}
@Test
public void testGet() {
load(5);
for (int i = 0; i < 5; ++i) {
assertEquals(Integer.valueOf(i), list.get(i));
}
list.rotate(2);
assertEquals(Integer.valueOf(3), list.get(0));
assertEquals(Integer.valueOf(4), list.get(1));
assertEquals(Integer.valueOf(0), list.get(2));
assertEquals(Integer.valueOf(1), list.get(3));
assertEquals(Integer.valueOf(2), list.get(4));
list.rotate(-4);
assertEquals(Integer.valueOf(2), list.get(0));
assertEquals(Integer.valueOf(3), list.get(1));
assertEquals(Integer.valueOf(4), list.get(2));
assertEquals(Integer.valueOf(0), list.get(3));
assertEquals(Integer.valueOf(1), list.get(4));
list.rotate(-8);
for (int i = 0; i < 5; ++i) {
assertEquals(Integer.valueOf(i), list.get(i));
}
}
@Test
public void testSet() {
load(4);
list.set(0, 3);
list.set(1, 2);
list.set(2, 1);
list.set(3, 0);
assertEquals(Integer.valueOf(3), list.get(0));
assertEquals(Integer.valueOf(2), list.get(1));
assertEquals(Integer.valueOf(1), list.get(2));
assertEquals(Integer.valueOf(0), list.get(3));
}
@Test
public void testAdd_int_GenericType() {
load(2);
list.add(1, 10);
list.add(0, 11);
list.add(4, 12);
assertEquals(Integer.valueOf(11), list.get(0));
assertEquals(Integer.valueOf(0), list.get(1));
assertEquals(Integer.valueOf(10), list.get(2));
assertEquals(Integer.valueOf(1), list.get(3));
assertEquals(Integer.valueOf(12), list.get(4));
}
@Test
public void testAdd_GenericType() {
load(2);
list.add(10);
assertEquals(Integer.valueOf(0), list.get(0));
assertEquals(Integer.valueOf(1), list.get(1));
assertEquals(Integer.valueOf(10), list.get(2));
}
@Test
public void testRemove_int() {
load(5);
list.remove(4);
list.remove(2);
list.remove(0);
assertEquals(Integer.valueOf(1), list.get(0));
assertEquals(Integer.valueOf(3), list.get(1));
list.clear();
load(5);
list.rotate(2);
list.remove(0);
assertEquals(Integer.valueOf(4), list.get(0));
assertEquals(Integer.valueOf(0), list.get(1));
assertEquals(Integer.valueOf(1), list.get(2));
assertEquals(Integer.valueOf(2), list.get(3));
}
@Test
public void testIndexOf() {
load(10);
list.rotate(-3);
assertEquals(1, list.indexOf(4));
}
@Test
public void testLastIndexOf() {
load(10);
list.rotate(-3);
assertEquals(8, list.lastIndexOf(1));
}
@Test
public void testSort() {
list.add(5);
list.add(2);
list.add(1);
list.add(4);
list.rotate(-1);
list.sort(Integer::compare);
assertEquals(Integer.valueOf(1), list.get(0));
}
@Test
public void testRemove_Object() {
load(5);
list.rotate(-1);
list.remove(Integer.valueOf(3));
assertEquals(4, list.size());
assertEquals(Integer.valueOf(1), list.get(0));
assertEquals(Integer.valueOf(2), list.get(1));
assertEquals(Integer.valueOf(4), list.get(2));
assertEquals(Integer.valueOf(0), list.get(3));
}
@Test
public void testRotate() {
load(10);
list.rotate(2);
assertEquals(Integer.valueOf(8), list.get(0));
assertEquals(Integer.valueOf(9), list.get(1));
assertEquals(Integer.valueOf(0), list.get(2));
list.rotate(-5);
assertEquals(Integer.valueOf(3), list.get(0));
assertEquals(Integer.valueOf(4), list.get(1));
assertEquals(Integer.valueOf(5), list.get(2));
}
}
Please, tell me anything that comes to mind.