I am currently learning about generics in Java, coming from C++ it's quite a difference.
I'd like to a vector addition, in C++, it would look something like this. (I know that this code is not well-written, it's just for a quick example to show what I want to do)
#include <iostream>
#include <vector>
template <typename T>
class Vect{
public:
std::vector<T> vect;
Vect(std::vector<T> t){vect = t;}
T index(int i){return vect[i];}
void print(){
for(int i = 0; i < vect.size(); ++i){
std::cout << vect[i] << " ";
}
std::cout << std::endl;
}
void add(Vect<T> other){
for(int i = 0; i < vect.size(); ++i){
vect[i] = vect[i]+other.index(i);
}
}
};
int main(){
std::vector<int> p1;
p1.push_back(1);
p1.push_back(2);
p1.push_back(3);
p1.push_back(4);
Vect<int> vec1 = Vect<int>(p1);;
Vect<int> vec2 = Vect<int>(p1);;
vec1.print();
vec2.print();
vec1.add(vec2);
vec1.print();
return 0;
}
I am trying to do the same with Java but I can't get a way to add two generics T and put the value (a T) in the first vector. I am doing this :
public class Vect0<T extends Number> {
//Attributs
private T[] _vec;
//Constructeur
public Vect0(T[] vec){
System.out.println("Construction du _vec !");
_vec = vec;
}
//Getter
public int get_length() {
return _vec.length;
}
//Methodes
public void print(){
System.out.print("[");
for (int i = 0; i < _vec.length; ++i){
if (i != _vec.length-1) {
System.out.print(_vec[i] + ", ");
}
else {
System.out.print(_vec[i]);
}
}
System.out.println("]");
}
public T index(int i) {
return _vec[i];
}
public void sum(Vect0<T> other) {
if (other.get_length() == this.get_length()) {
for(int i = 0; i < this.get_length(); ++i) {
Double res = (this.index(i).doubleValue() + other.index(i).doubleValue());
System.out.print(res);
T t = (T) res;
_vec[i] = t;
}
}
}
}
So it does print a double, but then the casting doesn't work and I get an error :
Exception in thread "main" java.lang.ArrayStoreException:
java.lang.Double at Vect0.sum(Vect0.java:37) at
Main.main(Main.java:12)
I hope you can help me figure this out.
Thank you very much.
Take a look at the code below. The explanation of your problem is in the comments.
public class TestGenerics {
public static void main(String[] args) {
Integer[] ints = new Integer[] { 1 };
Double[] doubles = new Double[] { 1.0 };
// By not telling the compiler which types you're
// expecting it will not be able to deduce if
// the usage of the generic methods are going
// to be ok or not, because the generics notation is
// erased and is not available at runtime.
// The only thing you will receive by doing this
// is a warning telling you that:
// "Vect0 is a raw type.
// References to generic type Vect0<T> should be parameterized"
Vect0 rawVect0 = new Vect0(ints);
Vect0 rawVect1 = new Vect0(doubles);
// This will throw java.lang.ArrayStoreException
// because you're trying, at runtime, to cast
// a Double object to Integer inside your sum method
// The compiler doesn't have a clue about that so it
// will blow at runtime when you try to use it.
// If you're only working with Integers, than you should not
// cast the result to double and should always work with intValues
rawVect0.sum(rawVect1);
// In Java, when using generics, you should be
// explict about your types using the diamond operator
Vect0<Integer> vect2 = new Vect0<>(ints);
Vect0<Double> vect3 = new Vect0<>(doubles);
// Now that you told the compiler what your types are
// you will receive a compile time error:
// "The method sum(Vect0<Integer>) in the type Vect0<Integer>
// is not applicable for the arguments (Vect0<Double>)"
vect2.sum(vect3);
}
}
If you read about the ArrayStoreException it will become clearer:
public class ArrayStoreException extends RuntimeException Thrown to
indicate that an attempt has been made to store the wrong type of
object into an array of objects. For example, the following code
generates an ArrayStoreException:
Object x[] = new String[3];
x[0] = new Integer(0);
Whenever dealing with Generic methods you should think that the Producer should use extends and the Consumer should use super - PECS. This is a good thing to know. Check out this question:
What is PECS (Producer Extends Consumer Super)?
One solution to your case, if you really want to be able to add different types of Number, is to always work inside the Vect0 object with one specific type (Double). Check out the code below:
public class Vect0<T extends Number> {
// Attributs
private Double[] _vec;
// Constructeur
public Vect0(T[] vec) {
System.out.println("Construction du _vec !");
if (vec instanceof Double[]) {
_vec = (Double[]) Arrays.copyOf(vec, vec.length);
} else {
_vec = Arrays.stream(vec).map(Number::doubleValue).toArray(Double[]::new);
}
}
// Getter
public int get_length() {
return _vec.length;
}
// Methodes
public void print() {
System.out.print("[");
for (int i = 0; i < _vec.length; ++i) {
if (i != _vec.length - 1) {
System.out.print(_vec[i] + ", ");
} else {
System.out.print(_vec[i]);
}
}
System.out.println("]");
}
public Double index(int i) {
return _vec[i];
}
public void sum(Vect0<T> other) {
if (other.get_length() == this.get_length()) {
for (int i = 0; i < this.get_length(); ++i) {
// Now you're only working with Doubles despite of the other object's true type
_vec[i] = index(i) + other.index(i);
}
}
}
}
And in order to use it, you should reference the objects using the super class Number, like this:
Vect0<Number> vect2 = new Vect0<>(ints1);
Vect0<Number> vect3 = new Vect0<>(doubles1);
// now it will be able to add two different types without complaining
vect2.sum(vect3);
If you want something more elaborate, you could start by checking out this question: How to add two java.lang.Numbers?
Cheers!
References:
Erasure of Generic Methods
ArrayStoreException
More on PECS
Related
I've been trying to search, but I can't quite find the answer to my problem. I feel like I've implemented my generic datatype correctly but I'm still getting an error.
I'm doing an exercise where I'm given constructors/methods and variables (so I can't change the variable inputs for instance), and now I have to write the constructors/methods to make them work. The program creates an array (objects) that can grow by adding elements to it, and the comments above each method in the code explains what they do.
When I try to create the arrays in my two constructors, I get the error "Variable must provide either dimension expressions or an array initializer" for the empty array and "Type mismatch: cannot convert from T[] to int".
In my "add" method I get the error "Cannot invoke add(int, T) on the array type T[]" and finally in my "toArray" method I get the error "Type mismatch: cannot convert from Object[] to T[]".
The common theme seems to be that I haven't implemented the generic datatype correctly, but no matter how many times I try to search for similar questions, I can't see how what I've done have been different.
When searching, I see many people say that an array can't "grow" and to instead use an ArrayList, however there has to be a way to solve this with just an array, otherwise you can't solve this exercise.
import java.util.ArrayList;
import java.util.Arrays;
public class GrowingArray<T> {
private Object[] objects;
private int pointer;
//empty array
public GrowingArray() {
objects = new Object[];
}
//array that contains ds
public GrowingArray(T[] ds) {
objects = new Object[ds];
}
// add element e and return true
public boolean add(T e) {
pointer = 0;
objects.add(pointer++, e);
return true;
}
// return true if value d is found in the array
public boolean contains(T d) {
for(int i = 0; i <= objects.length; i++) {
if(objects[i] == d) {
}
}
return true;
}
// return the element on index i
public T get(int index) {
int i = index;
return objects[i];
}
// return first index containing d, if not found return -1
public int indexOf(T d) {
for(int i = 0; i <= objects.length; i++) {
if(d == objects[i]) {
return i;
}
}
return -1;
}
// return last index containing d, if not found return -1
public int lastIndexOf(T d) {
for(int i = objects.length; i >= 0; i--) {
if(d == objects[i]) {
return i;
}
}
return -1;
}
// return length of array
public int size() {
return objects.length;
}
// return a trimmed version of the array
public T[] toArray() {
return Arrays.copyOf(objects, objects.length);
}
}
objects = new Object[];
"Variable must provide either dimension expressions or an array initializer"
You need to pass a size. Empty brackets don't work.
objects = new Object[someInitialSize];
objects = new Object[ds];
"Type mismatch: cannot convert from T[] to int"
To copy an array use Arrays.copyOf:
objects = Arrays.copyOf(ds, ds.length);
I'm still a little confused with regards to the difference between static and dynamic. From what I know dynamic uses object while static use type and that dynamic is resolved during runtime while static is during compile time. so shouldn't this.lastName.compareTo(s1.lastName) use dynamic binding instead?
key.compareTo(list[position-1]) use dynamic binding
public static void insertionSort (Comparable[] list)
{
for (int index = 1; index < list.length; index++)
{
Comparable key = list[index];
int position = index;
while (position > 0 && key.compareTo(list[position-1]) < 0) // using dynamic binding
{
list[position] = list[position-1];
position--;
}
list[position] = key;
}
}
Why does (this.lastName.compareTo(s1.lastName)) use static binding?
private String firstName;
private String lastName;
private int totalSales;
#Override
public int compareTo(Object o) {
SalePerson s1 = (SalePerson)o;
if (this.totalSales > s1.getTotalSales())
{
return 1;
}
else if (this.totalSales < s1.getTotalSales())
{
return -1;
}
else //if they are equal
{
return (this.lastName.compareTo(s1.lastName)); //why is this static binding??
}
}
Your question isn't complete and doesn't include all relevant the code. However this is the basic difference between the different bindings
Java has both static and dynamic binding. Binding refers to when variable is bound to a particular data type.
Static/Early binding is done at compile time for: private, final and static methods and variables. And also for overloaded methods
Dynamic/late binding is done at runtime for: methods which can be overriden methods. This is what enables polymorphic behaviour at runtime.
To further demonstrate this point have a look at this code and see if you can determine when it would be early and late binding:
/* What is the output of the following program? */
public class EarlyLateBinding {
public boolean equals(EarlyLateBinding other) {
System.out.println("Inside of overloaded Test.equals");
return false;
}
public static void main(String[] args) {
Object t1 = new EarlyLateBinding(); //1
Object t2 = new EarlyLateBinding(); //2
EarlyLateBinding t3 = new EarlyLateBinding(); //3
Object o1 = new Object();
Thread.currentThread().getStackTrace();
int count = 0;
System.out.println(count++);
t1.equals(t2);//n
System.out.println(count++);
t1.equals(t3);//n
System.out.println(count++);
t3.equals(o1);
System.out.println(count++);
t3.equals(t3);
System.out.println(count++);
t3.equals(t2);
}
}
Answer:
++ is after the count and hence the result returned is the 0 before incrementing it. Hence starts with 0 and proceeds as you expect.
The only scenario where the equals methods of EarlyLateBinding object
is actually invoked is is statement 3.
This is because the equals method is overloaded (Note: the different
method signature as compared to the object class equals)
Hence the type EarlyLateBinding is bound to the variable t3 at
compile time.
.
in this code
public static void insertionSort (Comparable[] list)
{
for (int index = 1; index < list.length; index++)
{
Comparable key = list[index];
int position = index;
while (position > 0 && key.compareTo(list[position-1]) < 0)
{
list[position] = list[position-1];
position--;
}
list[position] = key;
}
}
key can be anything that implements the Comparable interface so in the compile time compiler doesn't know the exact type so type is resolved in the runtime by using the object that key referring to.
But in this code,
#Override
public int compareTo(Object o) {
SalePerson s1 = (SalePerson)o;
if (this.totalSales > s1.getTotalSales())
{
return 1;
}
else if (this.totalSales < s1.getTotalSales())
{
return -1;
}
else //if they are equal
{
return (this.lastName.compareTo(s1.lastName));
}
}
compiler knows the type of the s1 so it use the static binding
Basically I would like to know if there is a way to "disable" an attribute within a block after a certain point.
For example check the following scenario:
for(int i=0;i<10;i++){
for(int j=i+5;j<50;j++){
//from here until end of the block I want to make sure I don't use **i** anymore.
print(j*5+i); //I want this line to produce compiler error
}
}
Don't get me wrong I understand it is a bad programming, but I still can't help but to use i,j,k,h as attributes. and sometimes I make a mistake by misplacing the attributes in wrong places.
Call a method.
for (int i = 0; i < 10; i++) {
for (int j = i + 5; j < 50; j++) {
doSomething();
}
}
...
private void doSomething() {
// Woot, no i and no j!
}
Your code doesn't make sense to anybody. You need to divide it into functions with good names so that anyone can understand what your program is doing without comments around the code or getting mixed up with variables.
Here's an example for the code you have posted:
public void printNumberTimes5(int number) {
print(number*5);
}
But don't stop there, make it obvious what the loop is doing too:
public void printSomeNumbers(int someNumber) {
for(int j=someNumber+5;j<50;j++){
printNumberTimes5(j);
}
}
And again:
public void printSomeNumbers_repeat(int repeat) {
for(int i=0;i<repeat;i++){
printSomeNumbers(i);
}
}
I don't really know what you're doing but renaming the function to what you're supposed to be doing would make it clear.
Remember: each function should only have one job.
Finally, give i and j real names so that you understand what those numbers do and don't mix them up.
The best way to obtain this in java, is by using scope. Make sure that the variables are in different scopes and then you don't have access to it. A good guideline to follow is to split your logic in various small methods, this way you'll ensure the desired behavior.
My recommendations in order of preference:
Use meaningful variable-names. Maybe i isn't as good as e.g. row, ...
Use functions to group operations and also reduce the variables they can access. This can also lead to a point where repeating operations can easily be reused.
Use a custom counter-object like this one
/**
* Created for http://stackoverflow.com/q/25423743/1266906
*/
public class ObliviousLoops {
public static void main(String[] args) {
for(LockableCounter i = new LockableCounter(0); i.getValue() < 42; i.unlock().increment()) {
System.out.println("A-loop:" + i.getValue());
i.lock();
// No access, everything is fine
}
for(LockableCounter i = new LockableCounter(0); i.getValue() < 42; i.unlock().increment()) {
System.out.println("B-loop1:" + i.getValue());
i.lock();
// Next statement will throw an Exception
System.out.println("B-loop2:" + i.getValue());
}
}
static class LockableCounter {
private long value;
private boolean locked;
LockableCounter(long value) {
this.value = value;
}
public LockableCounter lock() {
this.locked = true;
return this;
}
public LockableCounter unlock() {
this.locked = false;
return this;
}
public long getValue() {
if(locked) {
throw new IllegalStateException("Accessing locked counter");
}
return value;
}
public void increment() {
if(locked) {
throw new IllegalStateException("Accessing locked counter");
}
value++;
}
#Override
public String toString() {
if(locked) {
throw new IllegalStateException("Accessing locked counter");
}
return String.valueOf(value);
}
}
}
the most obvious draw-backs of the last solution is a less fluent handling of the value, less ways to optimize the operations for the compiler, ... in practice you may even want to replace the LockableCounter by something different, once you are sure you calculations are written as desired to speed things up.
Use Java 8's lambda-function to build something behaving similar to for-loops where you can null-out the counter for the rest of the cycle (actually this is a variant of #2)
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
/**
* Created for http://stackoverflow.com/q/25423743/1266906
*/
public class LambdaLoops {
public static void main(String[] args) {
iterate(0, 42, (i) -> {
System.out.println("A-loop:" + (i + 0));
i = null;
});
iterate(0, (i) -> i < 42, (i) -> ++i, (i) -> {
System.out.println("B-loop:" + (i + 0));
i = null;
});
iterate(0, (i) -> i < 42, (i) -> {
System.out.println("C-loop1:" + (i + 0));
i = null;
// Next statement will not throw an Exception
System.out.println("C-loop2:" + i);
// Next statement will throw an Exception
System.out.println("C-loop3:" + (i + 0));
});
}
static void iterate(Integer initial, Integer limit, Consumer<? super Integer> function) {
for (Integer i = initial; i < limit; i++) {
function.accept(i);
}
}
static void iterate(Integer initial, Predicate<? super Integer> when, Consumer<? super Integer> function) {
for (Integer i = initial; when.test(i); i++) {
function.accept(i);
}
}
static <T> void iterate(T initial, Predicate<? super T> when, Function<? super T, ? extends T> increment, Consumer<? super T> function) {
for (T i = initial; when.test(i); i = increment.apply(i)) {
function.accept(i);
}
}
}
as in #3 this will most likely lead to decreased performance, but has the advantage, that your IDE might alert you, that i will always be null. This should however be easier to optimize be inlining than #3 as there is no additional boolean involved. If and when the JIT does inlining is however hard to guess.
Since so many of answers talk about why this is a bad idea, so I won't repeat it.
One solution that comes to my mind is to use an counter object. Whenever you want a particular counter to go out of scope, set that to null. If you use it after this point, a Null pointer access warning is shown (at least in eclipse. I suspect other IDEs should also have this feature. Not sure whether javac generates a warning).
public class DisappearingVariables {
public static class Counter {
int i = 0;
public Counter() {
}
public void inc() {
i++;
}
public int get() {
return i;
}
}
public static void main(String[] args) {
for(Counter i = new Counter(), oi = i; i.get() < 10; i = oi, i.inc()) {
System.out.println("i = " + i.get());
i = null;
i.inc(); // This line gets a warning
for(int j = 0; j < 10; j++) {
}
}
}
}
I have a filter class wherein the user must declare the type (e.g. Filter<Double>, Filter<Float> etc). The class then implements a moving average filter so objects within the class must be added. My question is how to do this? I'm sorry if the answer is simple but I've muddled myself up by thinking about it too much I think :p.
public abstract class FilterData<T>
{
private final List<T> mFilter;
private T mFilteredValue; // current filtered value
protected Integer mSize = 10;
private T mUnfilteredValue; // current unfiltered value
public FilterData()
{
mFilter = new ArrayList<T>();
}
public FilterData(int size)
{
mSize = size;
mFilter = new ArrayList<T>(mSize);
}
public abstract T add(final T pFirstValue, final T pSecondValue);
#SuppressWarnings("unchecked")
public T filter(T currentVal)
{
T filteredVal;
mUnfilteredValue = currentVal;
push(currentVal);
T totalVal = (T) (new Integer(0));
int numNonZeros = 1;
for (int i = 0; i < mFilter.size(); ++i)
{
if (mFilter.get(i) != (T) (new Integer(0)))
{
++numNonZeros;
T totalValDouble = add(mFilter.get(i), totalVal);
totalVal = totalValDouble;
}
}
Double filteredValDouble = (Double) totalVal / new Double(numNonZeros);
filteredVal = (T) filteredValDouble;
mFilteredValue = filteredVal;
return filteredVal;
}
public T getFilteredValue()
{
return mFilteredValue;
}
public List<T> getFilterStream()
{
return mFilter;
}
public T getUnfilteredValue()
{
return mUnfilteredValue;
}
public void push(T currentVal)
{
mFilter.add(0, currentVal);
if (mFilter.size() > mSize)
mFilter.remove(mFilter.size() - 1);
}
public void resizeFilter(int newSize)
{
if (mSize > newSize)
{
int numItemsToRemove = mSize - newSize;
for (int i = 0; i < numItemsToRemove; ++i)
{
mFilter.remove(mFilter.size() - 1);
}
}
}
}
Am I right to include the abstract Add method and if so, how should I extend the class correctly to cover primitive types (e.g. Float, Double, Integer etc.)
Thanks
Chris
EDIT:
Apologies for being unclear. This is not homework I'm afraid, those days are long behind me. I'm quite new to Java having come from a C++ background (hence the expectation of easy operator overloading). As for the "push" method. I apologise for the add method in there, that is simply add a value to a list, not the variable addition I was referring to (made a note to change the name of my method then!). The class is used to provide an interface to construct a List of a specified length, populate it with variables and obtain an average over the last 'x' frames to iron out any spikes in the data. When a new item is added to the FilterData object, it is added to the beginning of the List and the last object is removed (provided the List has reached the maximum allowed size). So, to provide a continual moving average, I must summate and divide the values in the List.
However, to perform this addition, I will have to find a way to add the objects together. (It is merely a helper class so I want to make it as generic as possible). Does that make it any clearer? (I'm aware the code is very Mickey Mouse but I wanted to make it as clear and simple as possible).
What you're trying to do is create a Queue of Number objects with a fixed size, over which you want to calculate an average. With the trivial situation that you have size = 2 and store two integers 1 & 2 you have an average of 1.5 so its reasonable to set the return type of your filter method to double.
You can then write this code similar to this
public abstract class FilterData<T extends Number> {
private final Queue<T> mFilter = new LinkedList<T>();
protected Integer mSize;
public FilterData() {
this(10);
}
public FilterData(int size) {
mSize = size;
}
public double filter(T currentVal) {
push(currentVal);
double totalVal = 0d;
int numNonZeros = 0;
for (T value : mFilter) {
if (value.doubleValue() != 0) {
++numNonZeros;
totalVal += value.doubleValue();
}
}
return totalVal / numNonZeros;
}
public void push(T currentVal) {
mFilter.add(currentVal);
if (mFilter.size() > mSize)
mFilter.remove();
}
public void resizeFilter(int newSize) {
if (mSize > newSize) {
int numItemsToRemove = mSize - newSize;
for (int i = 0; i < numItemsToRemove; ++i) {
mFilter.remove();
}
}
mSize = newSize;
}
}
You should note that this isn't thread safe.
I'm having problems trying to pass an Integer object from a driver class as an argument for function of a SortedArray Generic class I created. From my driver class, I convert the user's int input into an Integer object to be cast onto Comparable of my SortedArray class.
I continue to receive the error: "Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to Comparable". I took a look at some of my classmates' source codes only to find little difference in setting the arguments/parameters yet they have their code working just fine. I've been looking for hours trying to find what error I've made and I still can't find why my Integer object can't be cast to Comparable.
Here's a bit from my SortedArray Class
public class SortedArray implements Comparable{
public int size;
public int increment;
public int top;
Comparable[] a = new Comparable [size];
public SortedArray(int initialSize, int incrementAmount)
{
top = -1;
size = initialSize;
increment = incrementAmount;
}
public int appropriatePosition(Comparable value)
{
int hold = 0;
if(top == -1)
{
return 0;
}
else
{
for(int i = 0; i <= top; i++)
{
if(a[i].compareTo(value) > 0)
{
hold = i;
break;
}
}
}
return hold;
}
public void insert(Comparable value) //The function that my driver class needs to access
{
//Shifting numbers to the top
if(full() == true)
{
Comparable[] tempArray = new Comparable[top + increment];
for(int i= 0; i< size; i++)
{
tempArray[i]= a[i];
a = tempArray;
}
size = top + increment;
}
if(a[appropriatePosition(value) + 1] != null)
{
for(int i = top; i < appropriatePosition(value); i--)
{
a[i + 1] = a[i];
}
}
a[appropriatePosition(value) + 1]= value;
}
Here's the code for my driver class that passes Integer Object insertObj as an argument for SortedArray's insert function.
public class IntDriver {
public static void main(String[] args)
{
Scanner keyboard = new Scanner(System.in);
//Creating variables
int data;
boolean check = false;
int choice;
int size = 5;
int increment = 3;
SortedArray b = new SortedArray(size, increment);
//Creating Menu
while(check == false)
{
System.out.println("Please choose through options 1-6.");
System.out.println("1. Insert\n2. Delete\n3. Clear\n4. Smallest\n5. Largest\n6. Exit");
choice = keyboard.nextInt();
switch(choice)
{
case 1:
System.out.println("Type the int data to store in array location.");
data = keyboard.nextInt();
Integer insertObj = new Integer(data);
b.insert(insertObj);// Here's where I lay "insertObj" as an argument for the SortedArray function.
System.out.println("The value " + data + " is inserted");
break;
The problem is that Integer extends java.lang.Comparable, then your Comparable is not a java.lang.Comparable. Look at the error message, your Comparable comes from the default package rather than java.lang:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to Comparable
That is, you can't cast java.lang.Integer to your own class
As mentioned by axtavt, the problem is that you have your own class Comparable. More specifically, what that means is that:
Integer.valueOf(1) instanceof java.util.Comparable == true
Integer.valueOf(1) instanceof Comparable == false
This means that somewhere in your code you have something like:
Object[] a = new Object[] {Integer.valueOf(1);};
Comparable x = (Comparable) a[0];
// or something equivalent, this is likely being passed through layers
// and not being done next to each other like this.
You need to change that to:
Object[] a = new Object[] {Integer.valueOf(1);};
java.util.Comparable x = (java.util.Comparable) a[0];
Even better, you should rename your Comparator class to something that doesn't collide with the standard classes in Java. In general, even though Java has namespacing, you should try to avoid your classes having the same name as the system classes to avoid exactly this kind of confusion.
I haven't put too much effort into this, but wouldn't it just be easier to use a SortedSet implementation (or its child interface NavigableSet) like TreeSet rather than write your own class? That is, unless you wanted duplicate elements...