PriorityQueue has objects with the same priority - java

I'm using a priority queue to sort and use a large number of custom objects. The objects have a "weight" that is their natural ordering. However, different objects that are inserted into the priority queue may have the same "weight". In such cases, I want the priority queue to order them in the same order in which they were put into the queue.
For example, if I add in CustomObjects A,B,C,D in that order, all with the same "weight", than the priority queue should return them in that order as well - even if I poll one or more of the objects before adding in the others.
Here is the CompareTo for my custom object:
public int compareTo(CustomObject o) {
int thisWeight = this.weight;
int thatWeight = o.weight;
if(thisWeight < thatWeight){
return -1;
}
else{
return 1;
}
}
While I thought that this would maintain that initial order, it doesn't. This occurs when I input A,B,C with weight 1; poll A; and add D,E also with weight 1. Somehow, D and E are sorted after B, but before C.
I am aware that the Iterator for PriorityQueues doesn't return the correct ordering, so I am limited in my ability to look at the ordering - however I can see the order that the elements leave the queue and it clearly doesn't follow the path that I want it to.
Suggestions?

If you need to have an ordering according the insertion order you need to use an extra element for timestamp.
I.e. on insertions and equal weight use timestamp to see which element was inserted first.
So CustomObject should be something like:
class CustomObject {
int weight;
long timestamp;
}
And the comparison should be:
public int compareTo (CustomObject o) {
int thisWeight = this.weight;
int thatWeight = o.weight;
if (thisWeight != thatWeight) {
return thisWeight - thatWeight;
}
else {
return this.timestamp - o.timestamp;
}
}
The smaller timestamp means it was inserted earlier so you keep in the insertion order.
You could also use a "logical" time by maintaining a counter that you update on each add or remove.

You could use an automatically-incremented sequence number as a secondary key, and use it to break ties.
Javadoc for PriorityBlockingQueue includes an example of this technique:
Operations on this class make no guarantees about the ordering of elements with equal priority. If you need to enforce an ordering, you can define custom classes or comparators that use a secondary key to break ties in primary priority values. For example, here is a class that applies first-in-first-out tie-breaking to comparable elements. To use it, you would insert a new FIFOEntry(anEntry) instead of a plain entry object.
class FIFOEntry<E extends Comparable<? super E>>
implements Comparable<FIFOEntry<E>> {
final static AtomicLong seq = new AtomicLong();
final long seqNum;
final E entry;
public FIFOEntry(E entry) {
seqNum = seq.getAndIncrement();
this.entry = entry;
}
public E getEntry() { return entry; }
public int compareTo(FIFOEntry<E> other) {
int res = entry.compareTo(other.entry);
if (res == 0 && other.entry != this.entry)
res = (seqNum < other.seqNum ? -1 : 1);
return res;
}
}

Related

create PriorityQueue in O(n ) with custom comparator

I was trying to implement MST with Priorityqueue with custom comparator, but I am facing problem in building min-heap with it in O(n) time. The problem is only one constructor of Priorityqueue allows to create PriorityQueue in O(n), but it does not take any comparator as argument. I want it to use my custom comparator. Is there workaround for this problem ? PriorityQueue.addAll() will lose the purpose of using Min-heap for MST as it is O(nlogn) method. Here is my code.
ArrayList <edge>ar=new ArrayList<>();
for(int i=0;i<e;i++)
{
int u=ss.nextInt();
int v=ss.nextInt();
int w=ss.nextInt();
ar.add(new edge(u,v,w));
}
PriorityQueue <edge>pr=new PriorityQueue<edge>(ar);
And the comparator that I want to use:-
PriorityQueue <edge>ar=new PriorityQueue(11,new Comparator() {
#Override
public int compare(Object o1, Object o2) {
edge n1=(edge) o1;
edge n2=(edge) o2;
if(n1.w<n2.w)
{
return -1;
}
else if(n1.w==n2.w)
{
if((n1.u+n1.v+n1.w)<=(n2.u+n2.v+n2.w))
{
return -1;
}
else
{
return 1;
}
}
else
{
return 1;
}
}
});
If you have not min-heap-ordered your list elsewhere, you will not be able to new PriorityQueue(...) anything and somehow avoid the hit of creating your heap. The math here says it's O(n) for the average case, but it is still more than just iterating.
PriorityQueue<edge> pr = new PriorityQueue<edge>(ar, comp) {
PriorityQueue(List<edge> ar, Comparator<edge> c) {
this(c);
for(int i = 0; i < queue.length; i++) {
queue[i] = ar.get(i);
}
this.size = queue.length;
heapify(); // O(n), except that heapify is private and thus you can't call it!!!
}
}
Now I haven't tested this, it's just off the top of my head with some guidance from the PriorityQueue source, but it should point you in the right direction.
But sometime you have to pay the piper and create the heap-order and that be more than just iteration. It should still be on O(n) though, because of heapify.
Another option is to have edge implement Comparable<edge>. Then you can just PriorityQueue<edge> pr = new PriorityQueue(ar);
If you cannot control edge implements Comparable<edge> then you could compose a container class:
class EdgeContainer implements Comparable<EdgeContainer> {
private static final Comparator<edge> comp = ; // that comparator above
private final edge edge;
EdgeContainer(Edge edge) { this.edge = edge; }
public int compareTo(EdgeContainer e) { return comp.compare(edge, e.edge); }
public edge getEdge() { return edge; }
}
List <EdgeContainer>ar=new ArrayList<>();
for(int i=0;i<e;i++)
{
int u=ss.nextInt();
int v=ss.nextInt();
int w=ss.nextInt();
ar.add(new EdgeContainer(new edge(u,v,w)));
}
PriorityQueue<EdgeContainer> qr = new PriorityQueue(ar);
Java's PriorityQueue takes O(n) time to create a priority queue out of a collection passed to it. The mathematical proof has been given in CLSR chapter 6.4 (page 157 in 3rd edition). Intuitively, as the underlying array is mutated into a heap using siftDown or siftUp, the size of the number of the elements to loop over for the next sift operation also decreases leading to an O(n) time complexity.
But as discussed in the comments and as you have mentioned in your question, you cannot achieve this time complexity by using addAll(). The reason is adAll() is inherited from AbstractQueue and works by adding elements in the collection one by one to the queue which can lead to O(nlogn) time complexity.
So if having O(n) time complexity is an absolute requirement, you will have no option but to implement the Comparator interface for the class of objects contained in the collection. #corsiKa's answer nicely details this approach. Also note that even if you pass a collection directly to PriorityQueue, it will convert it to an array which is basically another O(n) operation.

Using PriorityQueue with any Comparator in Java

Common question: How to use different Comparators of the custom class for sorting sequence its objects in PriorityQueue?
I tried to do that using this comparators in appropriate pairs of priorityqueues and lists of the objects with expected similar sorting results in the next code:
class User{
private Integer id;
private String name;
public User(Integer i, String n){
this.id=i;
this.name=n;
}
public Integer getId() {return id;}
public String getName() {return name;}
#Override
public boolean equals(Object obj) {
if (this == obj)return true;
if (obj == null)return false;
if (getClass() != obj.getClass())return false;
User other = (User) obj;
if(id == null){
if (other.id != null)return false;
}else if(!id.equals(other.id))return false;
return true;
}
#Override
public String toString() {return "[id:" + id + ", name:" + name + "]";}
}
public class MyPriorityQueue {
public static Comparator<User> cmpId = Comparator.comparingInt(x -> x.getId());
public static Comparator<User> cmpNameLength = Comparator.comparingInt(x -> x.getName().length());
public static void main(String[] args) {
List<User> users = new ArrayList<User>(10);
users.add(new User(1,"11111"));
users.add(new User(3,"333"));
users.add(new User(5,"5"));
users.add(new User(4,"44"));
users.add(new User(2,"2222"));
Queue<User> ids = new PriorityQueue<User>(10, cmpId); //use first comparator
users.forEach(x-> ids.offer(x));
Queue<User> names = new PriorityQueue<User>(10, cmpNameLength); //use second comparator
names.addAll(users);
System.out.println("Variant_1.1:");
ids.forEach(System.out::println);
System.out.println("Variant_2.1:");
names.forEach(System.out::println);
System.out.println("Variant_1.2:");
users.sort(cmpId); //use first comparator
users.forEach(System.out::println);
System.out.println("Variant_2.2:");
users.sort(cmpNameLength); //use second comparator
users.forEach(System.out::println);
}
}
Output:
Variant_1.1: //Failed sorted queue by user.id with using comporator cmpId
[id:1, name:11111]
[id:2, name:2222]
[id:5, name:5]
[id:4, name:44]
[id:3, name:333]
Variant_2.1: //Failed sorted queue by length of the user.name with cmpNameLength
[id:5, name:5]
[id:4, name:44]
[id:3, name:333]
[id:1, name:11111]
[id:2, name:2222]
Variant_1.2: // OK: correctly sorted list by user.id with cmpId comporator
[id:1, name:11111]
[id:2, name:2222]
[id:3, name:333]
[id:4, name:44]
[id:5, name:5]
Variant_2.2: //OK: for list by length of the user.name with cmpNameLength
[id:5, name:5]
[id:4, name:44]
[id:3, name:333]
[id:2, name:2222]
[id:1, name:11111]
I expected that the:
results of the variant 1.1 and 2.1;
results of the variant 1.2 and 2.2;
will be same, but they were different.
My questions: What have I done wrong for ordering priorytyqueue/comparator and How to get sorting result for the priorityqueue as for the appropriate list in my example?
You haven't done anything wrong, it's just that PriorityQueue's iterator is:
not guaranteed to traverse the elements of the priority queue in any particular order (Javadoc)
The forEach method internally uses the iterator, so the same problem exists.
This is because the underlying data structure is such that it "sorts" as you deque items. If the implementor wanted to return items in sorted order, they would have had to first collect the items, and then sort them before returning them. This incurs a performance hit, and so (I presume) it was decided to return it unordered, because PriorityQueue is primarily a queue, rather than a sorted collection, and a user could always sort the item themselves (which is as efficient as it gets).
In order to obtain the elements ordered, do something like:
while(pq.peek() != null){
System.out.println(pq.poll());
}
In your code sample, you are using Iterable#forEach to iterate through the queues.
ids.forEach(System.out::println);
names.forEach(System.out::println);
forEach ultimately delegates into Iterable#iterator. However, it's important to note that the subclass override in PriorityQueue#iterator has different JavaDocs with a special note about ordering.
Returns an iterator over the elements in this queue. The iterator does not return the elements in any particular order.
In other words, there is no guarantee that iterating over a PriorityQueue will use your Comparator. If instead you changed your code to drain the queue by repeatedly calling PriorityQueue#poll, then I expect you'd see results ordered according to your custom Comparator.
Digging into the OpenJDK source, we can see that the internal data structure inside PriorityQueue is a binary heap. This is backed by an array, and as callers add and remove elements of the queue, the code internally maintains the heap invariant.
/**
* Priority queue represented as a balanced binary heap: the two
* children of queue[n] are queue[2*n+1] and queue[2*(n+1)]. The
* priority queue is ordered by comparator, or by the elements'
* natural ordering, if comparator is null: For each node n in the
* heap and each descendant d of n, n <= d. The element with the
* lowest value is in queue[0], assuming the queue is nonempty.
*/
transient Object[] queue; // non-private to simplify nested class access
However, the internal Iterator implementation simply uses an integer cursor to scan forward through that array, with no consideration of element priorities or heap layout.
return (E) queue[lastRet = cursor++];

A TreeSet or TreeMap that allow duplicates

I need a Collection that sorts the element, but does not removes the duplicates.
I have gone for a TreeSet, since TreeSet actually adds the values to a backed TreeMap:
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
And the TreeMap removes the duplicates using the Comparators compare logic
I have written a Comparator that returns 1 instead of 0 in case of equal elements. Hence in the case of equal elements the TreeSet with this Comparator will not overwrite the duplicate and will just sort it.
I have tested it for simple String objects, but I need a Set of Custom objects.
public static void main(String[] args)
{
List<String> strList = Arrays.asList( new String[]{"d","b","c","z","s","b","d","a"} );
Set<String> strSet = new TreeSet<String>(new StringComparator());
strSet.addAll(strList);
System.out.println(strSet);
}
class StringComparator implements Comparator<String>
{
#Override
public int compare(String s1, String s2)
{
if(s1.compareTo(s2) == 0){
return 1;
}
else{
return s1.compareTo(s2);
}
}
}
Is this approach fine or is there a better way to achieve this?
EDIT
Actually I am having a ArrayList of the following class:
class Fund
{
String fundCode;
BigDecimal fundValue;
.....
public boolean equals(Object obj) {
// uses fundCode for equality
}
}
I need all the fundCode with highest fundValue
You can use a PriorityQueue.
PriorityQueue<Integer> pQueue = new PriorityQueue<Integer>();
PriorityQueue(): Creates a PriorityQueue with the default initial capacity (11) that orders its elements according to their natural ordering.
This is a link to doc: https://docs.oracle.com/javase/8/docs/api/java/util/PriorityQueue.html
I need all the fundCode with highest fundValue
If that's the only reason why you want to sort I would recommend not to sort at all. Sorting comes mostly with a complexity of O(n log(n)). Finding the maximum has only a complexity of O(n) and is implemented in a simple iteration over your list:
List<Fund> maxFunds = new ArrayList<Fund>();
int max = 0;
for (Fund fund : funds) {
if (fund.getFundValue() > max) {
maxFunds.clear();
max = fund.getFundValue();
}
if (fund.getFundValue() == max) {
maxFunds.add(fund);
}
}
You can avoid that code by using a third level library like Guava. See: How to get max() element from List in Guava
you can sort a List using Collections.sort.
given your Fund:
List<Fund> sortMe = new ArrayList(...);
Collections.sort(sortMe, new Comparator<Fund>() {
#Override
public int compare(Fund left, Fund right) {
return left.fundValue.compareTo(right.fundValue);
}
});
// sortMe is now sorted
In case of TreeSet either Comparator or Comparable is used to compare and store objects . Equals are not called and that is why it does not recognize the duplicate one
Instead of the TreeSet we can use List and implement the Comparable interface.
public class Fund implements Comparable<Fund> {
String fundCode;
int fundValue;
public Fund(String fundCode, int fundValue) {
super();
this.fundCode = fundCode;
this.fundValue = fundValue;
}
public String getFundCode() {
return fundCode;
}
public void setFundCode(String fundCode) {
this.fundCode = fundCode;
}
public int getFundValue() {
return fundValue;
}
public void setFundValue(int fundValue) {
this.fundValue = fundValue;
}
public int compareTo(Fund compareFund) {
int compare = ((Fund) compareFund).getFundValue();
return compare - this.fundValue;
}
public static void main(String args[]){
List<Fund> funds = new ArrayList<Fund>();
Fund fund1 = new Fund("a",100);
Fund fund2 = new Fund("b",20);
Fund fund3 = new Fund("c",70);
Fund fund4 = new Fund("a",100);
funds.add(fund1);
funds.add(fund2);
funds.add(fund3);
funds.add(fund4);
Collections.sort(funds);
for(Fund fund : funds){
System.out.println("Fund code: " + fund.getFundCode() + " Fund value : " + fund.getFundValue());
}
}
}
Add the elements to the arraylist and then sort the elements using utility Collections.sort,. then implement comparable and write your own compareTo method according to your key.
wont remove duplicates as well, can be sorted also:
List<Integer> list = new ArrayList<>();
Collections.sort(list,new Comparator<Integer>()
{
#Override
public int compare(Objectleft, Object right) {
**your logic**
return '';
}
}
)
;
I found a way to get TreeSet to store duplicate keys.
The problem originated when I wrote some code in python using SortedContainers. I have a spatial index of objects where I want to find all objects between a start/end longitude.
The longitudes could be duplicates but I still need the ability to efficiently add/remove specific objects from the index. Unfortunately I could not find the Java equivalent of the Python SortedKeyList that separates the sort key from the type being stored.
To illustrate this consider that we have a large list of retail purchases and we want to get all purchases where the cost is in a specific range.
// We are using TreeSet as a SortedList
TreeSet _index = new TreeSet<PriceBase>()
// populate the index with the purchases.
// Note that 2 of these have the same cost
_index.add(new Purchase("candy", 1.03));
Purchase _bananas = new Purchase("bananas", 1.45);
_index.add(new Purchase(_bananas);
_index.add(new Purchase("celery", 1.45));
_index.add(new Purchase("chicken", 4.99));
// Range scan. This iterator should return "candy", "bananas", "celery"
NavigableSet<PriceBase> _iterator = _index.subset(
new PriceKey(0.99), new PriceKey(3.99));
// we can also remove specific items from the list and
// it finds the specific object even through the sort
// key is the same
_index.remove(_bananas);
There are 3 classes created for the list
PriceBase: Base class that returns the sort key (the price).
Purchase: subclass that contains transaction data.
PriceKey: subclass used for the range search.
When I initially implemented this with TreeSet it worked except in the case where the prices are the same. The trick is to define the compareTo() so that it is polymorphic:
If we are comparing Purchase to PriceKey then only compare the price.
If we are comparing Purchase to Purchase then compare the price and the name if the prices are the same.
For example here are the compareTo() functions for the PriceBase and Purchase classes.
// in PriceBase
#Override
public int compareTo(PriceBase _other) {
return Double.compare(this.getPrice(), _other.getPrice());
}
// in Purchase
#Override
public int compareTo(PriceBase _other) {
// compare by price
int _compare = super.compareTo(_other);
if(_compare != 0) {
// prices are not equal
return _compare;
}
if(_other instanceof Purchase == false) {
throw new RuntimeException("Right compare must be a Purchase");
}
// compare by item name
Purchase _otherPurchase = (Purchase)_other;
return this.getName().compareTo(_otherChild.getName());
}
This trick allows the TreeSet to sort the purchases by price but still do a real comparison when one needs to be uniquely identified.
In summary I needed an object index to support a range scan where the key is a continuous value like double and add/remove is efficient.
I understand there are many other ways to solve this problem but I wanted to avoid writing my own tree class. My solution seems like a hack and I am surprised that I can't find anything else. if you know of a better way then please comment.

Does a PriorityQueue allow elements already within the queue to be reordered?

I want to augment or lower the priority of items in a PriorityQueue: for example, I might be downloading a long list of images and suddenly want the thirtieth one to have highest priority.
As I understand it, poll() always returns the queue object with the lowest value (as determined by a comparator). If I can lower the value of an item already in a queue (e.g. if this value is determined by an int in the object and I reduce the int value in some other function), will it be returned first by poll(), or is the sorting that allows poll() to do this done at insert time (e.g. by bubbling new queue elements down a list till they reach their "natural" depth)?
If this is done on a PriorityBlockingQueue, could it cause concurrency issues?
None of the collections in Java automatically reorders elements if you change the property that determine their order. For collections that depend on the .hashCode() , .equals() or some comparator, you are not allowed to change the object while it resides in the collection so that the hashcode/equals or comparator would yield different values.
You have to remove, change, re-insert the object if you want to change its priority within a PriorityQueue.
If you look at the source code, every time you poll() on a PriorityQueue it resifts, but it always returns the item that was at the top before the sift.
public class PQ {
int priority;
public PQ(int priority) {
this.priority = priority;
}
public static void main(String[] args) {
PQ one = new PQ(1);
PQ two = new PQ(2);
PQ three = new PQ(3);
PQ four = new PQ(4);
PQ five = new PQ(5);
PriorityQueue<PQ> q = new PriorityQueue<PQ>(3, new Comparator<PQ>() {
#Override
public int compare(PQ o1, PQ o2) {
return o1.priority-o2.priority;
}
});
q.add(three);
q.add(one);
q.add(four);
q.add(two);
q.add(five);
//Prints;
//PQ-1
//PQ-2
//PQ-3
//PQ-4
//PQ-5
while (!q.isEmpty()) {
System.out.println(q.poll());
}
q.add(three);
q.add(one);
q.add(four);
q.add(two);
q.add(five);
//Change the priority after it has been queued
four.priority = 10;
//Prints;
//PQ-1
//PQ-2
//PQ-3
//PQ-5
//PQ-10
while (!q.isEmpty()) {
System.out.println(q.poll());
}
//Reset the priority
four.priority = 4;
q.add(three);
q.add(one);
q.add(four);
q.add(two);
q.add(five);
//Change the priority after it has been queued
four.priority = 0;
//Prints;
//PQ-1
//PQ-0
//PQ-2
//PQ-3
//PQ-5
while (!q.isEmpty()) {
System.out.println(q.poll());
}
}
public String toString() {
return "PQ-" + priority;
}
}
If you iterate over a priority queue, you will find it is in no particular order (except the first element) If you want to change the order, I suggest you create another priority queue.
If you wan to change the position of one entry, I suggest you remove it, change its fields as required and add it again.

CompareTo may return 0, alternative to TreeSet/TreeMap

I need a sorted set of objects and am currently using the TreeSet. My problem is that the compareTo of the objects will often return 0, meaning the order of those two objects is to be left unchanged. TreeMap (used by TreeSet by default) will then regard them as the same object, which is not true.
What alternative to TreeMap can I use?
Use case: I have a set of displayable objects. I want to sort them by Y coordinate, so that they are rendered in the correct order. Of course, two objects may well have the same Y coordinate.
You're defining one criteria to compare, but you need to add extra criteria.
You say:
I have a set of displayable objects. I want to sort them by Y coordinate, so that they are rendered in the correct order. Of course, two objects may well have the same Y coordinate.
So, If two elements have the same Y coordinate, what you you put first? What would be the other criteria?
It may be the creation time, it may be the x coordinate, you just have to define it:
Map<String,Thing> map = new TreeMap<String,Thing>(new Comparator<Thing>(){
public int compare( Thing one, Thing two ) {
int result = one.y - two.y;
if( result == 0 ) { // same y coordinate use another criteria
result = one.x - two.x;
if( result == 0 ) { //still the same? Try another criteria ( maybe creation time
return one.creationTime - two.creationTime
}
}
return result;
}
});
You have to define when one Thing is higher / lower / equal / than other Thing . If one of the attributes is the same as other, probably you should not move them. If is there other attribute to compare the use it.
The issue you're running into is that compareTo returning 0 means that the objects are equal. At the same time, you're putting them into a set, which does not allow multiple copies of equal elements.
Either re-write your compareTo so that unequal elements return different values, or use something like a java.util.PriorityQueue which allows multiple copies of equal elements.
I've done this before. It's an ordered multi-map and it is just a TreeMap of List objects. Like this..
Map<KeyType, List<ValueType>> mmap = new TreeMap<KeyType, List<ValueType>>();
You need to construct a new LinkedList every time a new key is introduced, so it might be helpful to wrap it in a custom container class. I'll try to find something.
So, I threw this custom container together quickly (completely untested), but it might be what you are looking for. Keep in mind that you should only use this type of container if you are truly looking for an ordered map of value lists. If there is some natural order to your values, you should use a TreeSet as others have suggested.
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
public class MTreeMap<K, V> {
private final Map<K, List<V>> mmap = new TreeMap<K, List<V>>();
private int size = 0;
public MTreeMap() {
}
public void clear() {
mmap.clear();
size=0;
}
public boolean containsKey(K key) {
return mmap.containsKey(key);
}
public List<V> get(K key) {
return mmap.get(key);
}
public boolean isEmpty() {
return mmap.isEmpty();
}
public Set<K> keySet() {
return mmap.keySet();
}
public Collection<List<V>> valueLists() {
return mmap.values();
}
public void put(K key, V value) {
List<V> vlist = mmap.get(key);
if (null==vlist) {
vlist = new LinkedList<V>();
mmap.put(key, vlist);
}
vlist.add(value);
++size;
}
public List<V> remove(Object key) {
List<V> vlist = mmap.remove(key);
if (null!=vlist) {
size = size - vlist.size() ;
}
return vlist;
}
public int size() {
return size;
}
public String toString() {
return mmap.toString();
}
}
Here's a rudimentary test:
public class TestAnything {
public static void main(String[] args) {
MTreeMap<Integer, String> mmap = new MTreeMap<Integer, String>();
mmap.put(1, "Value1");
mmap.put(2, "Value2");
mmap.put(3, "Value3");
mmap.put(1, "Value4");
mmap.put(3, "Value5");
mmap.put(2, "Value6");
mmap.put(2, "Value7");
System.out.println("size (1) = " + mmap.get(1).size());
System.out.println("size (2) = " + mmap.get(2).size());
System.out.println("size (3) = " + mmap.get(3).size());
System.out.println("Total size = " + mmap.size());
System.out.println(mmap);
}
}
The output is this:
size (1) = 2
size (2) = 3
size (3) = 2
Total size = 7
{1=[Value1, Value4], 2=[Value2, Value6, Value7], 3=[Value3, Value5]}
I have one idea of my own, but it's more of a workaround
int compare(Object a, Object b) {
an = a.seq + (a.sortkey << 16); // allowing for 65k items in the set
bn = b.seq + (a.sortKey << 16);
return an - bn; // can never remember whether it's supposed to be this or b - a.
}
sortKey = what really matters for the sorting, for example an Y coordinate
seq = a sequence number assigned to objects when added to the set
There are 2 important things to remember when using sorted sets (e.g. TreeSet) :
1) They are sets; two equal elements are not allowed in the same collection
2) Equality must be consistent with the comparison mechanism (either comparator or comparable)
Therefore, in your case you should "break ties" by adding some secondary ordering criteria. For example: first use Y axis, then X, and then some unique object identifier.
See also http://eyalsch.wordpress.com/2009/11/23/comparators/

Categories