Skip to content

Instantly share code, notes, and snippets.

@joelgwebber
Created August 9, 2012 13:39
Show Gist options
  • Save joelgwebber/3304252 to your computer and use it in GitHub Desktop.
Save joelgwebber/3304252 to your computer and use it in GitHub Desktop.
/**
* Example usage.
*/
class Example {
public static void main(String[] args) {
String example = "foo/bar/baz";
Slice<String> s = new Slice<>(example.split("/"));
}
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.
}
}
import java.util.Collection;
import java.util.Iterator;
import mn.dragon.CollectionModel;
/**
* A simple collection similar to Go's built-in notion of "slices".
*
* 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.
*
* 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.
*
* 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.
*/
@SafeVarargs
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;
}
}
@aunger
Copy link

aunger commented Aug 9, 2012

It looks like the constructor, Slice(T[] array, int first, int last), tests len before setting it.

@dragonsinth
Copy link

Slightly surprised that the head..tail constructor copies the underlying array, even when there are no tail elements, since in the degenerate case it's just a Slice(Slice) ctor. Does the go version work that way?

Also... rangeCheck... like a boss! (Sorry, it had to be said.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment