Created
September 3, 2012 05:42
-
-
Save mgenov/3607027 to your computer and use it in GitHub Desktop.
A simple collection similar to Go's built-in notion of "slices".
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.Collection; | |
import java.util.Iterator; | |
/** | |
* A simple collection similar to Go's built-in notion of "slices". | |
* <p/> | |
* These are essentially bounded array objects with an immutable underlying store. Appending and | |
* inserting elements requires a new backing store to be allocated, but slicing out a contiguous | |
* subset just creates a new Slice that references the existing backing store. | |
* <p/> | |
* The contents of a slice are mutable (using {@link #set(int, Object)}), but keep in mind that | |
* other slices may share the backing store, and will see this change if it's within their range. | |
* <p/> | |
* Notes: | |
* - Does not implement equals() and hashCode() (who really depends upon this for collections?). | |
* - iterator() doesn't support remove(), because it doesn't make sense. | |
*/ | |
public class Slice<T> implements Iterable<T> { | |
private Object[] array; | |
private int first, len; | |
/** | |
* Creates a new Slice with the given length. | |
*/ | |
public Slice(int len) { | |
array = new Object[len]; | |
first = 0; | |
this.len = len; | |
} | |
/** | |
* Creates a new Slice using an existing array. The array is not copied, so changes to either the | |
* Slice or the array affect one-another. | |
*/ | |
public Slice(T[] array) { | |
this.array = array; | |
first = 0; | |
len = array.length; | |
} | |
/** | |
* Creates a new Slice from an existing collection. The collection is copied into a new backing | |
* array. | |
*/ | |
@SuppressWarnings("unchecked") | |
public Slice(Collection<? extends T> coll) { | |
this((T[]) coll.toArray(new Object[coll.size()])); | |
} | |
/** | |
* Creates a new Slice from a subset of an existing array. the array is not copied, so chagnes to | |
* either the Slice or the array affect one-another. | |
*/ | |
public Slice(T[] array, int first, int last) { | |
if ((first < 0) || (len < 0) || (first > array.length) || | |
(last > array.length) || (first > last)) { | |
throw new IndexOutOfBoundsException(); | |
} | |
this.array = array; | |
this.first = first; | |
this.len = last - first; | |
} | |
/** | |
* Creates a new Slice from an existing Slice, appending any number of further items to the end. | |
*/ | |
public Slice(Slice<T> head, T... tail) { | |
array = new Object[head.len + tail.length]; | |
System.arraycopy(head.array, head.first, array, 0, head.len); | |
System.arraycopy(tail, 0, array, head.len, tail.length); | |
first = head.first; | |
len = head.len + tail.length; | |
} | |
/** | |
* Gets the item at the given index. | |
*/ | |
@SuppressWarnings("unchecked") | |
public T get(int idx) { | |
return (T) array[rangeCheck(idx)]; | |
} | |
/** | |
* Sets the item at the given index. | |
*/ | |
public void set(int idx, T value) { | |
array[rangeCheck(idx)] = value; | |
} | |
/** | |
* Gets the length of this Slice. | |
*/ | |
public int len() { | |
return len; | |
} | |
/** | |
* Gets the first item of this Slice. | |
*/ | |
public T first() { | |
return get(0); | |
} | |
/** | |
* Gets the last item of this Slice. | |
*/ | |
public T last() { | |
return get(len - 1); | |
} | |
/** | |
* Returns a new slice with the first item removed. | |
*/ | |
public Slice<T> removeFirst() { | |
return slice(1, len); | |
} | |
/** | |
* Returns a new slice with the last item removed. | |
*/ | |
public Slice<T> removeLast() { | |
return slice(0, len - 1); | |
} | |
@Override | |
public Iterator<T> iterator() { | |
return new Iterator<T>() { | |
int i = first; | |
@Override | |
public boolean hasNext() { | |
return i < first + len; | |
} | |
@SuppressWarnings("unchecked") | |
@Override | |
public T next() { | |
return (T) array[i++]; | |
} | |
@Override | |
public void remove() { | |
throw new UnsupportedOperationException(); | |
} | |
}; | |
} | |
@SuppressWarnings("unchecked") | |
public Slice<T> slice(int first, int last) { | |
return new Slice<T>((T[]) array, this.first + first, this.first + last); | |
} | |
@Override | |
public String toString() { | |
StringBuilder s = new StringBuilder(); | |
s.append("["); | |
for (Iterator<T> it = iterator(); it.hasNext(); ) { | |
s.append(it.next()); | |
if (it.hasNext()) { | |
s.append(", "); | |
} | |
} | |
s.append("]"); | |
return s.toString(); | |
} | |
private int rangeCheck(int idx) { | |
if ((idx < 0) || (idx >= len)) { | |
throw new IndexOutOfBoundsException(); | |
} | |
return idx + first; | |
} | |
} | |
/** | |
* Example usage. | |
*/ | |
class Example { | |
public static void main(String[] args) { | |
Slice<Integer> values = new Slice<Integer>(new Integer[]{1, 2, 3, 4}); | |
System.out.println(values.slice(0, 2).removeFirst().first()); | |
} | |
static void parse(Slice<String> s) { | |
if (s.len() == 0) { | |
return; | |
} | |
doSomethingWith(s.first()); | |
parse(s.removeFirst()); | |
} | |
static void doSomethingWith(String s) { | |
// TODO: something interesting. | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment