views:

81

answers:

4

I need to sort lists of objects with a non-static comparator that uses a value from it's outer object field.

class A {
    public int x;
    public int y;
    public int z;

    public Comparator<A> scoreComparator = new Comparator<A>() {
        public compare(A o1, A o2) {
            // System.out.println("this: " + this);
            return (int) (x * o1.x - x * o2.x);
        }
    }

    public A(int _x, int _y, int _z) {
        x = _x;
        y = _y;
        z = _z;
    }
}

A var_1 = new A(1, 2, 3);
A var_2 = new A(5, 6, 7);
List<A> list = getMyListFromSomewhere();

// the following will produce different ordering
Collections.sort(list, var_1.scoreComparator);
Collections.sort(list, var_2.scoreComparator);

But for some reason this does not work properly. When I uncomment the println line in the comparator, it shows that the references are to A objects, but they are different within one sort() call, therefore the value of "x" is different. What am I doing wrong here?

+1  A: 

Can you explain why do you need the Comparatorto be non-static? Why not just the following?

    static class MyComparator implements Comparator {
        public compare(A o1, A o2) {
            // System.out.println("this: " + this);
            return o1.x - o2.x;
        }
    }

    public Comparator scoreComparator = new MyComparator();
gpeche
Your example does not account for the internal state of another object. What I want to achieve is different ordering depending on what instance of A I take as a "base". For example: I have objects v1 and v2 - both instances of A. I have a list [v3, v4, v5]. When I sort with a "base" v1, I get [v4, v5, v1], when I sort "based" on v2 I get [v5, v1, v4]. The order of the sorted list depends on internal field values of v1 and v2 respectively.
osjak
Well your code looks all right. Can you give some examples?
gpeche
And anyway I would make the comparator a static class and would pass "x" as a paremeter to its constructor.
gpeche
A: 

That depends on what you want to achieve. The code above doesn't work because you use different values of x when you create your A instances.

Each time, you create an instance of A, you also create an instance of the comparator which is tied to the instance of A. That means the x in the compare() method is either o1.x or o2.x.

I suggest to create a new class that implements the comparator and which has a field x to make it independent of A:

public class ScoreComparator implements new Comparator<A>() {
    private int x;
    public ScoreComparator(int x) { this.x = x; }
    public compare(A o1, A o2) {
        // System.out.println("this: " + this);
        return (int) (x * o1.x - x * o2.x);
    }
}
Aaron Digulla
"That means the x in the compare() method is either o1.x or o2.x." -- Didn't get this?!
Sidharth Panwar
When I pass "var_1.scoreComparator" as my comparator, doesn't that have a reference to var_1 already? So when JVM looks at "(x * o1.x - x * o2.x)" it would translate it to "(var_1.x * o1.x - var_1.x * o2.x)". Isn't it how it works?
osjak
I reread your comment and I think your solution will work great for me. I can certainly pass the value of "x" to the comparator. Thank you! But I still do not understand why my solution didn't work out.
osjak
As for my comment: When you create an `var_1`, you also get a comparator for `var_1`. In this comparator, you access `var_1.x`. When you sort the list containing `var_1` and `var_2` using the comparator from `var_1`, then you will get a call to `compare()` with `var_1` and `var_2` as the values of the parameters. That means the `return` becomes `var_1.x * var_1.x - var_1.x * var_2.x`.
Aaron Digulla
A: 

Let's see first what the scoreComparator does. The line

(int) (x * o1.x - x * o2.x)

could be also written as

(int) x * (o1.x - o2.x)

which means the the sign of x -- positive or negative inverts comparation result will revert the ordering in the sorting list.

Casting to int is added to ensure integer overflow if values of x and o1.x or x and o2.x are too big. Again, the sign of x will just revert the ordering.

Since both var_1 and var_2 have positive values for the field x we can conclude that the second scenario causes integer overflow and different ordering. var_1.x equals to 1, and var_2.x equals to 5 which makes integer overflowing five times more probable in the latter case.

Boris Pavlović
A: 

I'm not 100% sure what you want to achieve by this design, but this is a very bad design. If you want a non-static comparator inside the same class type, try going for compareTo rather than compare. Otherwise, put the compare method in a separate class as @Aaron suggested.

Sidharth Panwar