使用四种规则之一对列表进行排序的最佳实践

3

我有一系列需要按照规则排序的对象。但我需要能够随时更改规则,然而,我只有一组有限的排序规则。哪种数据结构是最好的选择?

例如,我有以下这个类:

class Test {
    public final int amount;
    public final int cost;
    public final String name;
    public final int whatever;
    // ...

    // TODO: add a constructor to set the fields :-)
}

我该如何存储这些字段以便按数量、成本、名称或其他方式对它们进行排序。但只能使用其中一种规则。

我可以想象使用ArrayListHashSet,在其中使用自定义的Comparator函数调用sort函数来实现。但我认为这样做效率不高,特别是在移动设备上。有更好的方法来实现吗?

5个回答

5

您不能使用Set进行排序,因为它没有任何顺序。然而,使用List和自定义的Comparator<T>概念是合理的。

你应该选择这个解决方案,并不关心此时的性能。如果你对结果不满意,那么尝试提出更好的解决方案。

最好的解决方案是按正确的顺序从存储中读取数据。我不知道你的应用程序如何存储这个结构。因此我无法帮助你。但是实现可比较的解决方案,你会发现这并不难。

在移动设备上重要的是内存使用情况。如果你的应用程序将使用很多这些排序操作,你可以将Comparators作为枚举创建,这样它们只会加载一次,并且可以简化代码。

private enum  TestComparator implements Comparator<Test> {
 BY_NAME {

    @Override
    public int compare(Test o1, Test o2) {

       //We validate first against null

       return o1n.name.compareTo(o2.name);

    }               
  }
 BY_WHATEVER{

    @Override
    public int compare(Test o1, Test o2) {

       //We validate first against null
      return (o1.whatever<o2.whatever ? -1 : (o1.whatever==o2.whatever ? 0 : 1));
    }               
  }

}

3
做这个:
class Test {
public final int amount;
public final int cost;
public final String name;
public final int whatever;
// ...

// TODO: add a constructor to set the fields :-)

    class TestAmountComparator implements Comparator<Test> {
        @Override
        public int compare(Test t1, Test t2) {
            return Integer.valueOf(t1.amount).compareTo(Integer.valueOf(t2.amount))          
        }
    }

    class TestCostComparator implements Comparator<Test> {
        @Override
        public int compare(Test t1, Test t2) {
            return Integer.valueOf(t1.cost).compareTo(Integer.valueOf(t2.cost))          
        }
    }

将您的测试对象存储在ArrayList(或任何其他集合)中,然后按照以下方式对它们进行排序:

List<Test> list = new ArrayList<Test>(myTest); //your Test list
//sorting
Collections.sort(list, new TestAmountComparator()); //sort by amount
Collections.sort(list, new TestCostComparator()); //sort by cost

2
比较"cost"使用Integer.compare(t1.cost, t2.cost)更有效率;"amount"同理。 - Evgeniy Dorofeev

1
我更倾向于在类中实现Comparable接口并实现compareTo方法。这提供了在不同数据结构之间排序的一致行为。 在这种情况下,只有在需要特殊排序时才会使用Comparator接口
class Test implements Comparable {
    public final int amount;
    public final int cost;
    public final String name;
    public final int whatever;
    // ...
    //add equals ,hashcode, and compareTo method in the class...
    // TODO: add a constructor to set the fields :-)
}

如果实例是唯一的并且已经实现了可比较性,您可以使用TreeSet。否则,您必须使用列表,并使用Collection.sort函数对它们进行排序。

您可以根据访问用途来决定数据结构。如果您想按顺序访问元素,请使用LinkedList,否则请使用ArrayList。


TreeSet的提示是可以的。但对于这个问题不行,因为OP需要在同一模型上使用不同的比较器。 - Damian Leszczyński - Vash
实际上,以例如数量排序的TreeSet可能具有相同数量的实例,如果compareTo在t1.amount == t2.amount时返回1。 - Evgeniy Dorofeev
有java.util.PriorityQueue,它也是排序的,但可以包含重复项。 - Evgeniy Dorofeev
@EvgeniyDorofeev Set 只能包含唯一的对象。如果两个对象相等,则第二个对象不会被添加到集合中。compareTo 的实现必须依赖于实现者... - Vishal
如果您想添加重复实例,则集合将丢弃第二个条目。 - Vishal
@Vash 我认为你说得对。但是它适用于处理排序类型。 :) - Vishal

1

我的版本:

class Test3 implements Comparable<Test3> {
    public int amount;
    //...

    Comparator<Test3> comparator;

    public void setComparator(Comparator<Test3> comparator) {
        this.comparator = comparator;
    }

    @Override
    public int compareTo(Test3 o) {
        return comparator.compare(this, o);
    }
}

0

这是如何在不丢失重复项的情况下使用TreeSet的方法:

import java.util.Comparator;
import java.util.TreeSet;

    public class Test {
        int amount;

        Test(int amount) {
            this.amount = amount;
        }

        public static void main(String args[]) throws Exception {
            Comparator<Test> c = new Comparator<Test>() {
                @Override
                public int compare(Test o1, Test o2) {
                    if (o1.amount >= o2.amount) {
                        return 1;
                    }
                    return -1;
                }
            };
            TreeSet<Test> s = new TreeSet<Test>(c);
            s.add(new Test(2));
            s.add(new Test(1));
            s.add(new Test(1));
            for (Test t : s) {
                System.out.println(t.amount);
            }

        }
    }

这将打印:

1
1
2

如果金额相等,您的比较器不会返回有效结果。 - Damian Leszczyński - Vash
但这就是为什么相等的对象会留在TreeSet中,整个过程才能正常工作。 - Evgeniy Dorofeev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接