Java 如何在不同时间按不同参数对列表进行排序

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1421322/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 11:54:04  来源:igfitidea点击:

How do I sort a list by different parameters at different timed

javasorting

提问by runaros

I have a class named Personwith multiple properties, for example:

我有一个Person用多个属性命名的类,例如:

public class Person {
    private int id;
    private String name, address;
    // Many more properties.
}

A lot of Person-objects are stored in an ArrayList<Person>. I want to sort this list by multiple sort parameters, and different from time to time. For instance I might one time want to sort by nameascending and then addressdescending, and another time just by iddescending.

许多Person-objects 存储在ArrayList<Person>. 我想通过多个排序参数对这个列表进行排序,并且不时不同。例如,我可能有一次想按name升序然后address降序排序,而另一次只是按id降序排序。

And I don't want to create my own sort methods (i.e., I want to use Collections.sort(personList, someComparator). What is the most elegant solution that achieves this?

而且我不想创建自己的排序方法(即,我想使用Collections.sort(personList, someComparator). 实现这一目标的最优雅的解决方案是什么?

采纳答案by Yishai

I think your enum approach is basically sound, but the switch statements really need a more object oriented approach. Consider:

我认为您的枚举方法基本上是合理的,但是 switch 语句确实需要一种更加面向对象的方法。考虑:

enum PersonComparator implements Comparator<Person> {
    ID_SORT {
        public int compare(Person o1, Person o2) {
            return Integer.valueOf(o1.getId()).compareTo(o2.getId());
        }},
    NAME_SORT {
        public int compare(Person o1, Person o2) {
            return o1.getFullName().compareTo(o2.getFullName());
        }};

    public static Comparator<Person> decending(final Comparator<Person> other) {
        return new Comparator<Person>() {
            public int compare(Person o1, Person o2) {
                return -1 * other.compare(o1, o2);
            }
        };
    }

    public static Comparator<Person> getComparator(final PersonComparator... multipleOptions) {
        return new Comparator<Person>() {
            public int compare(Person o1, Person o2) {
                for (PersonComparator option : multipleOptions) {
                    int result = option.compare(o1, o2);
                    if (result != 0) {
                        return result;
                    }
                }
                return 0;
            }
        };
    }
}

An example of usage (with a static import).

使用示例(使用静态导入)。

public static void main(String[] args) {
    List<Person> list = null;
    Collections.sort(list, decending(getComparator(NAME_SORT, ID_SORT)));
}

回答by runaros

One way is to create a Comparatorthat takes as arguments a list of properties to sort by, as this example shows.

一种方法是创建一个Comparator将要排序的属性列表作为参数的 ,如本例所示。

public class Person {
    private int id;
    private String name, address;

    public static Comparator<Person> getComparator(SortParameter... sortParameters) {
        return new PersonComparator(sortParameters);
    }

    public enum SortParameter {
        ID_ASCENDING, ID_DESCENDING, NAME_ASCENDING,
        NAME_DESCENDING, ADDRESS_ASCENDING, ADDRESS_DESCENDING
    }

    private static class PersonComparator implements Comparator<Person> {
        private SortParameter[] parameters;

        private PersonComparator(SortParameter[] parameters) {
            this.parameters = parameters;
        }

        public int compare(Person o1, Person o2) {
            int comparison;
            for (SortParameter parameter : parameters) {
                switch (parameter) {
                    case ID_ASCENDING:
                        comparison = o1.id - o2.id;
                        if (comparison != 0) return comparison;
                        break;
                    case ID_DESCENDING:
                        comparison = o2.id - o1.id;
                        if (comparison != 0) return comparison;
                        break;
                    case NAME_ASCENDING:
                        comparison = o1.name.compareTo(o2.name);
                        if (comparison != 0) return comparison;
                        break;
                    case NAME_DESCENDING:
                        comparison = o2.name.compareTo(o1.name);
                        if (comparison != 0) return comparison;
                        break;
                    case ADDRESS_ASCENDING:
                        comparison = o1.address.compareTo(o2.address);
                        if (comparison != 0) return comparison;
                        break;
                    case ADDRESS_DESCENDING:
                        comparison = o2.address.compareTo(o1.address);
                        if (comparison != 0) return comparison;
                        break;
                }
            }
            return 0;
        }
    }
}

It can then be used in code for instance like this:

然后可以在代码中使用它,例如:

cp = Person.getComparator(Person.SortParameter.ADDRESS_ASCENDING,
                          Person.SortParameter.NAME_DESCENDING);
Collections.sort(personList, cp);

回答by KLE

Comparators lets you do that very easily and naturally. You can create single instances of comparators, either in your Person class itself, or in a Service class associated to your need.
Examples, using anonymous inner classes:

比较器让您可以非常轻松自然地做到这一点。您可以在 Person 类本身或与您的需要关联的 Service 类中创建比较器的单个实例。
示例,使用匿名内部类:

    public static final Comparator<Person> NAME_ASC_ADRESS_DESC
     = new Comparator<Person>() {
      public int compare(Person p1, Person p2) {
         int nameOrder = p1.getName().compareTo(p2.getName);
         if(nameOrder != 0) {
           return nameOrder;
         }
         return -1 * p1.getAdress().comparedTo(p2.getAdress());
         // I use explicit -1 to be clear that the order is reversed
      }
    };

    public static final Comparator<Person> ID_DESC
     = new Comparator<Person>() {
      public int compare(Person p1, Person p2) {
         return -1 * p1.getId().comparedTo(p2.getId());
         // I use explicit -1 to be clear that the order is reversed
      }
    };
    // and other comparator instances as needed... 


If you have many, you can also structure your comparators codeany way you like. For example, you could:

如果你有很多,你也可以用你喜欢的任何方式构建你的比较器代码。例如,您可以:

  • inherit from another comparator,
  • have a CompositeComparator that agregates some existing comparators
  • have a NullComparator that handle null cases, then delegates to another comparator
  • etc...
  • 从另一个比较器继承,
  • 有一个 CompositeComparator 来聚合一些现有的比较器
  • 有一个处理空情况的 NullComparator,然后委托给另一个比较器
  • 等等...

回答by Tom Hawtin - tackline

One approach would be to compose Comparators. This could be a library method (I'm sure it exists somewhere out there).

一种方法是组合Comparators。这可能是一个库方法(我确定它存在于某处)。

public static <T> Comparator<T> compose(
    final Comparator<? super T> primary,
    final Comparator<? super T> secondary
) {
    return new Comparator<T>() {
        public int compare(T a, T b) {
            int result = primary.compare(a, b);
            return result==0 ? secondary.compare(a, b) : result;
        }
        [...]
    };
}

Use:

用:

Collections.sort(people, compose(nameComparator, addressComparator));

Alternatively, note that Collections.sortis a stable sort. If performance isn't absolutely crucial, you sort be the secondary order before the primary.

或者,请注意这Collections.sort是一种稳定的排序。如果性能不是绝对重要的,那么在主要顺序之前排序是次要顺序。

Collections.sort(people, addressComparator);
Collections.sort(people, nameComparator);

回答by gia

I think coupling the sorters to the Person class, like in your answer, isn't a good idea, because it couples the comparison (usually business driven) and the model object to close to each other. Each time you want to change/add something the sorter, you need to touch the person class, which is usually something you do not want to do.

我认为将排序器耦合到 Person 类,就像在您的回答中一样,不是一个好主意,因为它将比较(通常是业务驱动的)和模型对象耦合在一起以使其彼此接近。每次你想改变/添加排序器的东西时,你需要触摸 person 类,这通常是你不想做的事情。

Using a Service or something similar, which provides Comparator instances, like KLE proposed, sounds way more flexible and extensible.

使用 Service 或类似的东西,它提供 Comparator 实例,就像 KLE 提出的那样,听起来更灵活和可扩展。

回答by Tadeusz Kopec

You can create comparators for each of properties you might want to sort and then try "comparator chaining" :-) like this:

您可以为您可能想要排序的每个属性创建比较器,然后尝试“比较器链接”:-),如下所示:

public class ChainedComparator<T> implements Comparator<T> {
    private List<Comparator<T>> simpleComparators; 
    public ChainedComparator(Comparator<T>... simpleComparators) {
        this.simpleComparators = Arrays.asList(simpleComparators);
    }
    public int compare(T o1, T o2) {
        for (Comparator<T> comparator : simpleComparators) {
            int result = comparator.compare(o1, o2);
            if (result != 0) {
                return result;
            }
        }
        return 0;
    }
}

回答by Constantin

I recently wrote a Comparator to sort multiple fields within a delimited String record. It allows you to define the delimiter, record structure and sorting rules (some of which are type-specific). You can use this by converting a Person record into a delimited String.

我最近编写了一个 Comparator 来对分隔字符串记录中的多个字段进行排序。它允许您定义分隔符、记录结构和排序规则(其中一些是特定于类型的)。您可以通过将 Person 记录转换为分隔字符串来使用它。

Required information is seeded to the Comparator itself, either programmatically or through an XML file.

所需信息以编程方式或通过 XML 文件提供给 Comparator 本身。

XML is validated by a package embedded XSD file. For example, below is a tab delimited record layout with four fields (two of which are sortable):

XML 由包嵌入的 XSD 文件验证。例如,下面是一个带有四个字段(其中两个可排序)的制表符分隔记录布局:

<?xml version="1.0" encoding="ISO-8859-1"?> 
<row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <delimiter>&#009;</delimiter>

    <column xsi:type="Decimal">
        <name>Column One</name>
    </column>

    <column xsi:type="Integer">
        <name>Column Two</name>
    </column>

    <column xsi:type="String">
        <name>Column Three</name>
        <sortOrder>2</sortOrder>
        <trim>true</trim>
        <caseSensitive>false</caseSensitive>        
        <stripAccents>true</stripAccents>
    </column>

    <column xsi:type="DateTime">
        <name>Column Four</name>
        <sortOrder>1</sortOrder>
        <ascending>true</ascending>
        <nullLowSortOrder>true</nullLowSortOrder>
        <trim>true</trim>
        <pattern>yyyy-MM-dd</pattern>
    </column>

</row>

You would then use this in java like so:

然后你可以像这样在java中使用它:

Comparator<String> comparator = new RowComparator(
              new XMLStructureReader(new File("layout.xml")));

Library can be found here:

图书馆可以在这里找到:

http://sourceforge.net/projects/multicolumnrowcomparator/

http://sourceforge.net/projects/multicolumnrowcomparator/

回答by ravi ranjan

Suppose a class Coordinateis there and one has to sort it in both ways according to X-coordinate and Y-coordinate. Two differnet comparators is needed for it. Below is the sample

假设有一个类Coordinate,并且必须根据 X 坐标和 Y 坐标以两种方式对其进行排序。它需要两个不同的比较器。下面是示例

class Coordinate
{

    int x,y;

    public Coordinate(int x, int y) {
        this.x = x;
        this.y = y;
    }

    static Comparator<Coordinate> getCoordinateXComparator() {
        return new Comparator<Coordinate>() {

            @Override
            public int compare(Coordinate Coordinate1, Coordinate Coordinate2) {
                if(Coordinate1.x < Coordinate2.x)
                    return 1;
                else
                    return 0;
            }
            // compare using Coordinate x
        };
    }

    static Comparator<Coordinate> getCoordinateYComparator() {
        return new Comparator<Coordinate>() {

            @Override
            public int compare(Coordinate Coordinate1, Coordinate Coordinate2) {
                if(Coordinate1.y < Coordinate2.y)
                    return 1;
                else
                    return 0;
            }
            // compare using Coordinate y
        };
    }
}

回答by oRUMOo

My approach is build on Yishai's. The main gap is that there is no way to sort first ascending for an attribute and after that decending for an other one. This can not be done with enumerations. For that I used classes. Because the SortOrder strongly depends on the type I prefered to implement it as an inner class of person.

我的方法是建立在 Yishai 的基础上。主要的差距是没有办法先对一个属性进行升序排序,然后再对另一个属性进行降序排序。这不能用枚举来完成。为此,我使用了类。因为 SortOrder 强烈依赖于我更喜欢​​将它实现为 person 的内部类的类型。

The class 'Person' with inner class 'SortOrder':

具有内部类 'SortOrder' 的类 'Person':

import java.util.Comparator;

public class Person {
    private int id;
    private String firstName; 
    private String secondName;

    public Person(int id, String firstName, String secondName) {
        this.id = id;
        this.firstName = firstName;
        this.secondName = secondName;   
    }

    public abstract static class SortOrder implements Comparator<Person> {
        public static SortOrder PERSON_ID = new SortOrder() {
            public int compare(Person p1, Person p2) {
                return Integer.valueOf(p1.getId()).compareTo(p2.getId());
            }
        };
        public static SortOrder PERSON_FIRST_NAME = new SortOrder() {
            public int compare(Person p1, Person p2) {
                return p1.getFirstName().compareTo(p2.getFirstName());
            }
        };
        public static SortOrder PERSON_SECOND_NAME = new SortOrder() {
            public int compare(Person p1, Person p2) {
                return p1.getSecondName().compareTo(p2.getSecondName());
            }
        };

        public static SortOrder invertOrder(final SortOrder toInvert) {
            return new SortOrder() {
                public int compare(Person p1, Person p2) {
                    return -1 * toInvert.compare(p1, p2);
                }
            };
        }

        public static Comparator<Person> combineSortOrders(final SortOrder... multipleSortOrders) {
            return new Comparator<Person>() {
                public int compare(Person p1, Person p2) {
                    for (SortOrder personComparator: multipleSortOrders) {
                        int result = personComparator.compare(p1, p2);
                        if (result != 0) {
                            return result;
                        }
                    }
                    return 0;
                }
            };
        }
    }

    public int getId() {
        return id;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getSecondName() {
        return secondName;
    }

    @Override
    public String toString() {
        StringBuilder result = new StringBuilder();

        result.append("Person with id: ");
        result.append(id);
        result.append(" and firstName: ");
        result.append(firstName);
        result.append(" and secondName: ");
        result.append(secondName);
        result.append(".");

        return result.toString();
    }
}

An example for using the class Person and its SortOrder:

使用类 Person 及其 SortOrder 的示例:

import static multiplesortorder.Person.SortOrder.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import multiplesortorder.Person;

public class Application {

    public static void main(String[] args) {
        List<Person> listPersons = new ArrayList<Person>(Arrays.asList(
                 new Person(0, "...", "..."),
                 new Person(1, "...", "...")
             ));

         Collections.sort(listPersons, combineSortOrders(PERSON_FIRST_NAME, invertOrder(PERSON_ID)));

         for (Person p: listPersons) {
             System.out.println(p.toString());
         }
    }
}

oRUMOo

或RUMOo