java按可指定属性对对象列表进行排序

Posted

技术标签:

【中文标题】java按可指定属性对对象列表进行排序【英文标题】:java sort list of objects by specifiable attribute 【发布时间】:2015-06-23 01:32:21 【问题描述】:

我想按对象的指定属性对对象列表进行排序,并且我想选择应该使用哪个属性进行排序。示例:

class Car
  private String name;
  private String colour;
  public enum sortBy NAME, COLOUR;

  public String name()
    return name;
  

  public String colour()
    return colour;
  

  public static Car[] getSortedArray(Car[] carArray, sortBy sortType)
    HashMap<Object, Car> carMap = new HashMap<Object, Car>();
    Object[] sortArray = new Object[carArray.length];
    Object value = null;
    for(int i = 0; i < carArray.length; i++)
      if(sortType == sortBy.NAME)
        value = carArray[i].name();
      else if(sortType == sortBy.COLOUR)
        value = carArray[i].colour();
      
      carMap.put(value, carArray[i]);
      sortArray[i] = value;
      
    Arrays.sort(sortArray);
    Car[] sortedArray = new Car[sortArray.length];
    for(int i = 0; i < sortArray.length; i++)
      sortedArray[i] = carMap.get(sortArray[i]);
    
    return sortedArray;
  


//external:
Car[] cars = getSomeCars();
Car[] nameSortedCars = Car.getSortedArray(cars, Car.sortBy.NAME);
Car[] colourSortedCars = Car.getSortedArray(cars, Car.sortBy.COLOUR);

这个想法很简单: 我将所有要排序的值放入一个数组中,然后创建一个映射,将这些值映射回它们的对象。对这个数组进行排序后,我将映射到这些值的对象以相同的顺序放入一个新数组中,然后按这些值排序。这些值是用 Object 类型创建的,所以我可以按多种类型排序(不仅仅是示例中的字符串)。

这很好用,除非您有两个具有相同属性值的对象,那么返回的数组中只会有一个对象,但会出现两次。 有没有更好的方法来实现这种排序?

【问题讨论】:

问题是,HashMap 不接受重复的键,那么如果你有 2 个具有相同键的对象,第二个会超速第一个。 【参考方案1】:

使用自定义比较器会简单得多:

name排序:

Arrays.sort(carArray, Comparator.comparing(Car::name));

colour排序:

Arrays.sort(carArray, Comparator.comparing(Car::colour));

所以你可以修改getSortedArray():

public static Car[] getSortedArray(Car[] carArray, Comparator<Car> comparator) 
    Car[] sorted = carArray.clone()
    Arrays.sort(sorted, comparator);
    return sorted;

然后这样称呼它:

Car[] sorted = getSortedArray(carArray, Comparator.comparing(Car::name));

编辑:

如果您使用的语言版本不支持这些功能,则可以通过显式创建实现Comparator 接口的嵌套类来创建比较器。

例如,这是一个单例 Comparator,它将 Car 实例与 name 进行比较:

static enum ByName implements Comparator<Car> 
    INSTANCE;

    @Override
    public int compare(Car c1, Car c2) 
        return c1.name().compareTo(c2.name());
    

然后调用:

Car[] sorted = getSortedArray(carArray, ByName.INSTANCE);

【讨论】:

这看起来可以解决我的问题,但它似乎不适用于 android。我将 Java 8 用作 AndroidStudio 的 JDK,但它似乎不知道 Comparator.comparing 和 Car::name 给我一个错误,即此语言级别不支持方法引用。 @jklmnn 我更新了答案。这应该适用于较旧的 Java 版本。 好的,这似乎是一个 Android 问题,我用汽车类创建了一个小测试,并且有效。谢谢!【参考方案2】:

TL;DR:已经有***了。

我想说最简单的方法是创建一个比较器:

final Comparator<Car> byName = Comparator.comparing(Car::name);
final Comparator<Car> byColour = Comparator.comparing(Car::colour);

然后只需在Arrays 上使用适当的方法按比较器进行排序:

Arrays.sort(carArray, byName);

现在你想用enum 来做吗?只要有enumimplements Comparator&lt;Car&gt;

enum SortBy implements Comparator<Car> 
    NAME(Comparator.comparing(Car::name)),
    COLOUR(Comparator.comparing(Car::colour));

    private final Comparator<Car> delegate;

    private SortBy(Comparator<Car> delegate) 
        this.delegate = delegate;
    

    @Override
    public int compare(final Car o1, final Car o2) 
        return delegate.compare(o1, o2);
                   

想按name 排序然后按 colour?简单:

final Comparator<Car> byName = SortBy.NAME.thenComparing(SortBy.COLOUR);

想要以倒序按名称排序吗?简单:

final Comparator<Car> byName = SortBy.NAME.reversed();

【讨论】:

【参考方案3】:

您正在重新发明***!如果你使用模板化的 Collections API,你的生活会轻松很多。为此,您将使用 List 而不是数组,定义一个 Comparator 来进行排序,然后让 API 为您完成工作。

 Comparator<Car> carComparator = new Comparator<Car>()
    public int sort(Car car1, Car car2)
      //Sorting logic goes here.
    
 
 List<Car> cars = getCars();
 cars = Collections.sort(cars, carComparator); //the cars collection is now sorted.

如果您有时想按一个或另一个属性排序,您可以将我的变量 carComparator 放入它自己的类中,并在构造函数中定义要按哪些属性排序。

希望有帮助:)

编辑:正如其他人指出的那样,这种方法也适用于数组。但除非您有充分的理由使用数组,否则使用集合通常会更容易。

【讨论】:

【参考方案4】:

我认为如果您将 Comparator 实现传递给 Arrays.sort,该解决方案会更有效。现在,从外观上看,您正在循环 n*2,哈希映射 (O(1)) 加上 Arrays.sort (另一个 0(n log n) 等)。如果您执行以下操作,您可以跳过当前使用的 2 个循环和地图。

您可以简单地创建一个比较器,如(粗略的代码):

class CarComparator implements Comparator<Car> 
    enum compareType; //plus setter

    public int compareTo(Car a, Car b) 
        if(compareType == COLOUR) return a.colour.compareTo(b.colour);
        if(compareType == NAME.....
    

,然后简单地将 Cars 数组发送到

Arrays.sort(cars, new CarComparator(COLOUR))

,或者使用更专业的比较器类,一个用于每个属性,一个工厂来渲染它们,当然如果这种情况经常发生,不要为每个排序创建一个新的 Comparator()。 :-)

总体而言,这种方法应该使您的代码更有效率。

【讨论】:

以上是关于java按可指定属性对对象列表进行排序的主要内容,如果未能解决你的问题,请参考以下文章

剑道网格按可空属性排序

Python入门题045:根据对象属性进行排序

Python中元祖对象排序

java使用stream对日期排序

如何根据对象的属性对对象列表进行排序?

如何根据对象的属性对对象列表进行排序?