java按可指定属性排序对象列表
我想按这些对象的指定属性对对象列表进行排序,我想选择应该用于排序的属性。 例:
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 carMap = new HashMap(); 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类型创建的,因此我可以按多种类型排序(不仅仅是示例中的字符串)。
除非你有两个具有相同属性值的对象,否则只有一个对象将在返回的数组中,但是两次。
有没有更好的方法来实现这种排序?
使用自定义比较器会简单得多:
按name
排序:
Arrays.sort(carArray, Comparator.comparing(Car::name));
按colour
排序:
Arrays.sort(carArray, Comparator.comparing(Car::colour));
所以你可以修改getSortedArray()
:
public static Car[] getSortedArray(Car[] carArray, Comparator comparator) { Car[] sorted = carArray.clone() Arrays.sort(sorted, comparator); return sorted; }
并称之为:
Car[] sorted = getSortedArray(carArray, Comparator.comparing(Car::name));
编辑:
如果使用不支持这些function的语言版本,则可以通过显式创建实现Comparator
接口的嵌套类来创建Comparator
器。
例如,这是一个单例Comparator
,它按name
比较Car
实例:
static enum ByName implements Comparator { INSTANCE; @Override public int compare(Car c1, Car c2) { return c1.name().compareTo(c2.name()); } }
然后打电话:
Car[] sorted = getSortedArray(carArray, ByName.INSTANCE);
TL; DR:已经有了一个轮子。
我想说最简单的方法是创建一个比较器:
final Comparator byName = Comparator.comparing(Car::name); final Comparator byColour = Comparator.comparing(Car::colour);
然后在Arrays
上使用适当的方法按比较器排序:
Arrays.sort(carArray, byName);
现在你想用enum
做吗? 只需让enum
implements Comparator
:
enum SortBy implements Comparator { NAME(Comparator.comparing(Car::name)), COLOUR(Comparator.comparing(Car::colour)); private final Comparator delegate; private SortBy(Comparator delegate) { this.delegate = delegate; } @Override public int compare(final Car o1, final Car o2) { return delegate.compare(o1, o2); } }
想按name
排序, 然后按 colour
排序? 简单:
final Comparator byName = SortBy.NAME.thenComparing(SortBy.COLOUR);
想按相反的顺序按名称排序 ? 简单:
final Comparator byName = SortBy.NAME.reversed();
你正在重新发明轮子! 如果您使用模板化的Collections API,生活将变得更加容易。 为此,您将使用List而不是数组,定义Comparator来进行排序,然后让API为您完成工作。
Comparator carComparator = new Comparator (){ public int sort(Car car1, Car car2){ //Sorting logic goes here. } } List cars = getCars(); cars = Collections.sort(cars, carComparator); //the cars collection is now sorted.
如果您希望有时按一个属性或另一个属性排序,可以将我的变量carComparator放入其自己的类中,并在构造函数中定义要排序的属性。
希望有所帮助:)
编辑:正如其他人所指出的,这种方法也适用于数组。 但除非你有充分的理由使用Arrays,否则使用Collections通常会更容易。
我认为如果将Comparator实现传递给Arrays.sort,解决方案会更有效。 现在,你从它的外观循环n * 2,哈希映射(O(1))加上Arrays.sort(这是另一个0(n log n)等)。 如果您执行以下操作,则可以跳过当前正在使用的2个循环和地图。
你可以简单地创建一个像(粗略代码)的比较器:
class CarComparator implements Comparator { enum compareType; //plus setter public int compareTo(Car a, Car b) { if(compareType == COLOUR) return a.colour.compareTo(b.colour); if(compareType == NAME..... } }
,然后简单地发送汽车数组
Arrays.sort(cars, new CarComparator(COLOUR))
,或使用更专业的比较器类,每个属性一个,以及渲染它们的工厂,当然,如果经常发生这种情况,不要为每种类型创建新的Comparator()。 🙂
总的来说,这种方法应该使您的代码更有效。 }