更改java类的排序条件的好方法

时间:2017-01-16 17:12:37

标签: java sorting treeset

我有一个代表一列的类。它有一个比较器,看起来像这样:

class Column
{
    int xposition;
    int usage;

    @Override
    public int compare(Object arg0, Object arg1) 
    {
         // sort logic
    }
}

我有TreeSetColumn个。我想先按TreeSet排序x-position,然后按usage排序。

我尝试创建一个超级类,例如Column2,它扩展了Column并具有不同的compare方法。然而,这使得从Column转换为Column2(反之亦然)非常难看。我还想到Column中的一个标志,指示如何进行排序,但这意味着修改所有对象以更改排序标准。

有没有更好的方法呢?

4 个答案:

答案 0 :(得分:4)

我会在一组外部Comparator中使用比较逻辑来表示您拥有的不同排序案例,然后在您想要更改排序时创建一个新的TreeSet

class Column
{
    int xposition;
    int usage;

    public static final Comparator<Column> SortByX = new Comparator<Column>() {
        @Override
        public int compare(Column c1, Column c2)
        {
            return Integer.compare(c1.xposition, c2.xposition);
        }
    };

    public static final Comparator<Column> SortByUsage = new Comparator<Column>() {
        @Override
        public int compare(Column c1, Column c2)
        {
            return Integer.compare(c1.usage, c2.usage);
        }
    };
}

TreeSet<Column> cols = new TreeSet<>(Column.SortByX);

然后,要更改排序:

TreeSet<Column> updated = new TreeSet<>(Column.SortByUsage);
updated.addAll(cols);
cols = updated;

如果在多线程环境中发生这种情况,则进行适当的同步。

无论您做什么,都不要使用可变状态更改对象Comparator的行为。如果你这样做,你可能很容易失去跟踪&#34;将对象放入像TreeSet这样的集合之后的对象。

答案 1 :(得分:0)

策略模式

您想要实现的目标似乎是战略模式的经典用例(例如Real World Example of the Strategy Pattern

本质上,您希望将此比较函数打包成可以放在列类的单独字段中的东西 - 具有实现Comparable的单个函数的普通类将起作用。然后,您的列只会将调用委托给该字段中存储的任何比较器。

答案 2 :(得分:0)

这是Guava&#39; ComparisionChain的确切用例:

采取的示例from here

public int compareTo(Foo that) {
     return ComparisonChain.start()
     .compare(this.xposition, that.xposition)
     .compare(this.usage, that.usage)
     .result();
}

答案 3 :(得分:0)

就像Sean Bright回答一样,我使用外部Comparator,如果你正在使用Java 8,你可以很容易地做到:

public static final Comparator<Foobar> NAME_THEN_AGE = 
  Comparators.comparing(Foobar::getName, String.CASE_INSENSITIVE_ORDER)
             .thenComparing(Foobar::getAge)
;

....
TreeSet<Foobar> foobar = new TreeSet<>(NAME_THEN_AGE);

但是,更好的是,在子类上覆盖Comparable通常是一个坏主意 - 也许它应该在父类上final或者应该创建一个受保护的compareTo0(A)做共同的工作(避免通过父类比较对象)。

有理由这样做,其中一个如下(来自Comparable.compareTo的Javadoc):

  

实现者必须确保sgn(x.compareTo(y))==   -sgn(y.compareTo(x))表示所有x和y。 (这意味着如果y.compareTo(x)抛出一个x.compareTo(y)必须抛出异常   异常。)

我们假设您class BC延长A而A实施Comparable<A>

class A implements Comparable<A> {
  @Override
  public int compareTo(A other) {return ...;}
}
class B extends A {
  @Override
  public int compareTo(A other) {return compareToAsB(((B)other));}
}
class C extends A {
  @Override
  public int compareTo(A other) {return compareToAsC(((C)other));}
}

A::compareTo返回的内容并不重要。 compareToAsBcompareToAsC都没有。

问题在于:

A a = ...;
B b = ...;
C c = ...;

a.compareTo(b); // ok
a.compareTo(c); // ok
b.compareTo(a); // ko ClassCastException
b.compareTo(c); // ko ClassCastException
c.compareTo(a); // ko ClassCastException
c.compareTo(b); // ko ClassCastException

如javadoc所引用,a.compareTo(b)应该抛出ClassCastException

此外,Java代码(Collections.sort)中还有一部分需要确保 sgn(x.compareTo(y))== -sgn(y.compareTo(x))对于所有x和y