Based on your comments I think really all you need is a customized ArrayList.
On a side note, based on your comments to other answers it seems you need to do a lot of random access by index and not by parameter. That is, you indicated you want to get an Edge by the order in which it was added rather than by specifying unique-ifying parameters, and that you may want to access that order in a highly non-sequential way. For the random access case, this data structure's performance will be better than the custom Set proposed above, because it pays a high upfront-cost of O(N) insert time, where N grows with the size of the set; however, when you go to do random access you get the same great O(1) by index retrieval time that an ArrayList
offers. In comparison, for the custom Set, even if you extend a LinkedHashSet to maintain access order you'd pay O(1) insert time, but O(N), to iterate through the entries in order, random access time.
Basically, you pay the unique-ifying constraint upfront, and forget about it later. In the case of the set, you pay an access time constraint later. Of course, that's not the end of the story, there is definitely a solution which might let both worlds be satisfied. Suppose we keep more data (ah, memory vs. CPU/time), and in the data structure below instead of searching through the list to find out if we already have the Edge in question we maintain an internal Set as well. When we go to insert, we first try finding the Edge in the Set (O(1)), when the Edge is unfound, we add it to the Set and to the List. Now, we have to keep an extra Set around (that's the memory cost) to avoid a time penalty for looking at our List during inserts.
So far so good, but we now need to also define a hashing function which generates unique hashCodes for, well, what you consider unique. In other words, we want to make sure, loosely: hashCode(Edge a) != hashCode(Edge b)
where a != b
should hold for some unbelievably large space of a
and b
being different in some sense you define. Given you define such a function you still have to pay the memory cost for keeping a set, but that's maybe not so bad if you really care about the insert cost.
Here's the simple straight List concept, it's easily modified to also maintain a Set, and you would modify the contains
function below to check the Set rather than check search the List if you wanted to go with the hybrid approach.
public class EdgeArrayList extends ArrayList<Edge>{
@Override
public boolean add(Edge e){
if(contains(e)){
return false;
}
super.add(e);
return true;
}
@Override
public boolean contains(Object o){
if(!(o instanceof Edge))
return false;
Edge e = (Edge) o;
for(Edge item : this){
if(item.source == e.source &&
item.destination == e.destination &&
item.weight == e.weight){
return true;
}
}
return false;
}
}
Usage would then be as you suggest, where Edges already contained in the EdgeArrayList
will simply not be added:
EdgeArrayList foo = new EdgeArrayList();
foo.add(new Edge(0, 0, 0));
foo.add(new Edge(0, 0, 0));
At the end of this foo
contains just the first instance of an Edge with all parameters set to 0 (I've tested this, it really works).
Some pitfalls here -- checking that a new Edge
is not the list will incur an O(N) hit on the order of the size of the current list N.