package net.varkhan.base.containers.list;
import net.varkhan.base.containers.Container;
import net.varkhan.base.containers.Iterator;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.NoSuchElementException;
/**
* <b></b>.
* <p/>
*
* @author varkhan
* @date 3/18/12
* @time 6:27 PM
*/
public class ArrayList<Type> implements List<Type>, Externalizable, Cloneable {
private static final long serialVersionUID=1L;
/**
* The list growth factor > 1.0
*/
protected final double growthfact;
/**
* The number of elements contained in the list
*/
protected long size=0;
/**
* The list slot storage
*/
private Object[] list=null;
/**********************************************************************************
** List constructors
**/
/**
* Creates a new ArrayList, specifying the reallocation strategy.
*
* @param gf the internal element array growth factor
*/
public ArrayList(double gf) {
if(gf<=1) gf=1.5;
this.growthfact=gf;
clear();
}
/**
* Creates a new ArrayList.
*/
public ArrayList() {
this(1.5);
}
/**
* Copies a Container, specifying the reallocation strategy.
*
* @param gf the internal element array growth factor
* @param list the Container to copy
*/
public ArrayList(double gf, Container<Type> list) {
this(gf);
this.list = new Object[(int)list.size()+1];
Iterator<? extends Type> it=list.iterator();
while(it.hasNext()) {
Type obj=it.next();
this.list[(int)this.size++]=obj;
}
}
/**
* Copies a Container.
*
* @param list the IndexedList to copy
*/
public ArrayList(Container<Type> list) {
this(1.5,list);
}
/**
* Builds an ArrayList from an array.
*
* @param gf the internal element array growth factor
* @param array the array to copy
*/
public ArrayList(double gf, Type... array) {
this(gf);
this.list = new Object[array.length+1];
System.arraycopy(array,0,this.list,0,array.length);
}
/**
* Builds an ArrayList from an array.
*
* @param array the array to copy
*/
public ArrayList(Type... array) {
this(1.5,array);
}
/**********************************************************************************
** List statistics accessors
**/
/**
* Returns the number of elements in this list.
*
* @return the number of entries (objects and related index) stored in this list
*/
public final long size() {
return size;
}
/**
* Indicates whether this list is empty.
*
* @return {@literal true} if this list contains no entry,
* {@literal false} otherwise
*/
public final boolean isEmpty() {
return size<=0;
}
/**
* Deletes all elements from this list.
*/
public void clear() {
size=0;
list=null;
}
/**********************************************************************************
** List entries accessors
**/
/**
* Gets an element from this list.
*
* @param idx the index of the element
*
* @return the element at position {@code idx}, or {@literal null} if no element exists
*/
@SuppressWarnings( { "unchecked" })
public Type get(long idx) {
if(idx<0||idx>=size) return null;
final Object val=list[(int) idx];
return (Type) val;
}
/**
* Adds an element to this list.
*
* @param elt the element to add at the end of the list
*
* @return {@literal true} if the list has been modified as a result of
* this operation, {@literal false} if the list remains unchanged
*/
public boolean add(Type elt) {
return add(size, elt);
}
/**
* Adds an element to this list.
*
* @param idx the index of the element that will be moved toward the end of the list
* @param elt the element to set
*
* @return {@literal true} if the list has been modified as a result of
* this operation, {@literal false} if the list remains unchanged (for
* instance, because the element was not initially in the list)
*/
public boolean add(long idx, Type elt) {
if(idx<0 || idx>size) return false;
if(list==null) {
list=new Object[(int) (idx+1)];
}
else if(size>=list.length) {
// We need to realloc
int newLen=(int) ((list.length+1)*growthfact);
if(newLen<=idx) newLen=(int) (idx+1);
Object[] newlist=new Object[newLen];
if(idx>0) System.arraycopy(list, 0, newlist, 0, (int)idx);
if(idx<size) System.arraycopy(list, (int)idx, newlist, (int)idx+1, (int)(size-idx));
list=newlist;
}
else if(idx<size) {
System.arraycopy(list, (int)idx, list, (int)idx+1, (int)(size-idx));
}
size++;
list[(int) idx]=elt;
return true;
}
/**
* Sets an element in this list.
*
* @param idx the index of the element to replace
* @param elt the element to set
*
* @return {@literal true} if the list has been modified as a result of
* this operation, {@literal false} if the list remains unchanged (for
* instance, because the element was not initially in the list)
*/
public boolean set(long idx, Type elt) {
if(idx<0 || idx>size) return false;
if(list==null) {
list=new Object[(int) (idx+1)];
}
else if(idx>=list.length) {
// We need to realloc
int newLen=(int) ((list.length+1)*growthfact);
if(newLen<=idx) newLen=(int) (idx+1);
Object[] newlist=new Object[newLen];
System.arraycopy(list, 0, newlist, 0, list.length);
list=newlist;
}
if(idx==size) size++;
list[(int) idx]=elt;
return true;
}
/**
* Removes an element from this list.
*
* @param idx the index of element to remove
*
* @return {@literal true} if the list has been modified as a result of
* this operation, {@literal false} if the list remains unchanged
*/
public boolean del(long idx){
if(idx<0||idx>=size) return false;
size--;
System.arraycopy(list,(int)(idx+1),list,(int)idx,(int)(size-idx));
if(size*growthfact*growthfact<list.length) {
// We need to realloc
int newLen=(int) ((list.length+1)/growthfact);
Object[] newlist=new Object[newLen];
System.arraycopy(list, 0, newlist, 0, (int)size+1);
list=newlist;
}
return true;
}
/**
* Removes an element from this list.
*
* @param elt the element to remove
*
* @return {@literal true} if the container has been modified as a result of
* this operation, {@literal false} if the container remains unchanged
*/
public boolean del(Type elt) {
long idx=0;
while(idx<size) {
Object obj=list[(int) idx];
if(elt==obj|| (elt!=null&&elt.equals(obj))) {
size--;
System.arraycopy(list,(int)(idx+1),list,(int)idx,(int)(size-idx));
if(size*growthfact*growthfact<list.length) {
// We need to realloc
int newLen=(int) ((list.length+1)/growthfact);
Object[] newlist=new Object[newLen];
System.arraycopy(list, 0, newlist, 0, (int)size+1);
list=newlist;
}
return true;
}
}
return false;
}
/**********************************************************************************
** List entries iterators
**/
/**
* Iterates over all elements in the list.
*
* @return an iterator over all the elements stored in the list
*/
public Iterator<? extends Type> iterator() {
return new Iterator<Type>() {
private int pos=0;
public boolean hasNext() {
return pos<size;
}
@SuppressWarnings("unchecked")
public Type next() {
if(pos<size) return (Type) list[pos++];
throw new NoSuchElementException("No element at index "+pos);
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
/**
* Iterate over each element of the list, and pass it as argument to a
* visitor's {@link Visitor#invoke} method, until this method returns
* a negative count.
*
* @param vis the visitor
* @param par the control parameter
* @param <Par> the type of the control parameter
*
* @return the sum of all positive return values from the visitor
*/
public <Par> long visit(Visitor<Type,Par> vis, Par par) {
long c=0;
int i=0;
while(i<size) {
@SuppressWarnings("unchecked")
Type obj=(Type) list[i];
long r=vis.invoke(obj, par);
if(r<0) return c;
c+=r;
i++;
}
return c;
}
@Override
public List<Type> sublist(long beg, long end) {
return new SubList((int)beg,(int)(end-beg));
}
protected class SubList implements List<Type> {
private final int beg;
private int len;
public SubList(int beg, int len) {
this.beg=beg;
this.len=len;
}
public long size() { return list==null?0:len; }
public boolean isEmpty() { return list==null||len==0; }
public void clear() { }
public boolean add(Type elt) {
if(ArrayList.this.add(beg+len, elt)) {
len ++;
return true;
}
return false;
}
public boolean add(long idx, Type elt) {
if(idx<0 || idx>len) return false;
if(ArrayList.this.add(beg+idx, elt)) {
len ++;
return true;
}
return false;
}
public Type get(long idx) {
if(idx<0 || idx>=len) return null;
return ArrayList.this.get(beg+idx);
}
public boolean set(long idx, Type elt) {
if(idx<0 || idx>len) return false;
if(idx==len) len++;
return ArrayList.this.set(beg+idx,elt);
}
public boolean del(long idx) {
if(idx<0 || idx>=len) return false;
if(ArrayList.this.del(beg+idx)) {
len --;
return true;
}
return false;
}
public boolean del(Type elt) { return false; }
public Iterator<? extends Type> iterator() {
return new Iterator<Type>() {
private volatile int pos=0;
public boolean hasNext() { return list!=null&&pos<len; }
@SuppressWarnings("unchecked")
public Type next() { if(list==null||pos>=len) throw new NoSuchElementException();
return (Type) list[beg+pos++];
}
public void remove() { throw new UnsupportedOperationException(); }
};
}
public <Par> long visit(Visitor<Type,Par> vis, Par par) {
long ret=0;
if(list!=null) for(int i=0;i<len;i++) {
@SuppressWarnings("unchecked")
Type obj=(Type) list[beg+i];
long r=vis.invoke(obj, par);
if(r<0) return ret;
ret+=r;
}
return ret;
}
public List<Type> sublist(long beg, long end) {
return new SubList((int)(this.beg+beg),(int)(end-beg));
}
public String toString() {
StringBuilder buf=new StringBuilder();
buf.append('[');
boolean first = true;
for(int i=0;i<len;i++) {
@SuppressWarnings("unchecked")
Type obj=(Type) list[beg+i];
if(first) first=false;
else buf.append(',');
buf.append(' ').append(obj);
i++;
}
buf.append(' ').append(']');
return buf.toString();
}
public int hashCode() {
int hash=0;
hash^=len;
if(len>0) {
for(int i=0;i<len;i++) {
Object o=list[beg+i];
if(o!=null) hash^=o.hashCode();
}
}
return hash;
}
public boolean equals(Object obj) {
if(!(obj instanceof List)) return false;
List<?> that=(List<?>) obj;
if(list==null) return that.size()==0;
if(len!=that.size()) return false;
if(len>0) {
for(int i=0;i<len;i++) {
Object thiso=list[beg+i];
Object thato=that.get(i);
if(thiso==thato) continue;
if(thiso==null||thato==null) return false;
if(!thiso.equals(thato)) return false;
}
}
return true;
}
}
/**********************************************************************************
** Externalization
**/
/**
* Write an ArrayList to a stream.
*
* @param out the stream to write the object to
*
* @throws java.io.IOException if I/O errors occur
* @serialData <li/> {@code Object defVal} - the default value
* <li/> {@code byte blockshift} - the block size 2-logarithm
* <li/> {@code double growthfact} - the buffer growth factor
* <li/> {@code long size} - the number of set entries
* <li/> {@code long head} - the highest allocated index + 1
* <li/> all the data block, as an occupancy count on one {@code int},
* followed by the values ({@code blocksize} {@code Object}s)
*/
public void writeExternal(ObjectOutput out) throws IOException {
out.writeLong(size);
out.writeLong(size);
out.writeObject(null);
if(size>0) {
for(int i=0;i<size;i++) {
out.writeObject(list[i]);
}
}
}
/**
* Read an ArrayList from a stream.
*
* @param in the stream to read the object from
*
* @throws IOException if I/O errors occur
*/
@SuppressWarnings( { "unchecked" })
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
size=in.readLong();
in.readLong(); // discard
in.readObject(); // discard
if(size>0) {
list=new Object[(int) size];
for(int i=0;i<size;i++) {
list[i]=in.readObject();
}
}
}
/**********************************************************************************
** Object method overrides
**/
/**
* Return a hash code value for this list.
*
* @return a hash code
*/
public int hashCode() {
int hash=0;
hash^=size^(size>>>32);
if(size>0) {
for(int i=0;i<size;i++) {
Object o=list[i];
if(o!=null) hash^=o.hashCode();
}
}
return hash;
}
public boolean equals(Object obj) {
if(!(obj instanceof List)) return false;
List<?> that=(List<?>) obj;
if(size!=that.size()) return false;
if(size>0) {
for(int i=0;i<size;i++) {
Object thiso=list[i];
Object thato=that.get(i);
if(thiso==thato) continue;
if(thiso==null||thato==null) return false;
if(!thiso.equals(thato)) return false;
}
}
return true;
}
@SuppressWarnings("unchecked")
public ArrayList<Type> clone() {
ArrayList<Type> clone;
try { clone=(ArrayList<Type>) super.clone(); }
catch(CloneNotSupportedException e) { return null; }
if(this.list!=null) {
clone.list=new Object[this.list.length];
System.arraycopy(this.list, 0, clone.list, 0, this.list.length);
}
return clone;
}
/**
* Returns a string representation of the list.
*
* @return a string enclosing in square brackets the string representations
* of all the elements in the list
*/
public String toString() {
StringBuilder buf=new StringBuilder();
buf.append('[');
boolean first = true;
for(int i=0;i<size;i++) {
@SuppressWarnings("unchecked")
Type obj=(Type) list[i];
if(first) first=false;
else buf.append(',');
buf.append(' ').append(obj);
}
buf.append(' ').append(']');
return buf.toString();
}
}