2015-04-16 6 views
3

Я хочу отсортировать список объектов по указанному атрибуту этих объектов, и я хочу выбрать, какой атрибут должен использоваться для сортировки. Пример: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<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, поэтому я могу сортировать по нескольким типам (а не только по строкам, как в примере).

Это прекрасно работает, если у вас нет двух объектов с одинаковым значением атрибута, тогда только один объект будет в возвращенном массиве, но два раза.
Есть ли лучший способ добиться этой сортировки?

+1

проблема заключается в том, что HashMap не принимает дублированные ключи, а если у вас есть 2 объекта с одним и тем же ключом, второй будет перегружать первый. – lcjury

ответ

6

Было бы гораздо проще использовать пользовательские компараторы:

сортировки по 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)); 

Edit:

Если вы используете версию языка, который не поддерживает эти функции, вы можете создать компараторы, явно создавая вложенный класс, который реализует интерфейс 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); 
+0

Похоже, что это решит мою проблему, но, похоже, для Android это не работает. Я применил Java 8 как JDK для AndroidStudio, но, похоже, не знает, что Comparator.comparing и Car :: name дают мне ошибку, что ссылки на методы не поддерживаются на этом языке. – jklmnn

+0

@jklmnn Я обновил ответ. Это должно работать на старых версиях Java. –

+0

Ок, похоже, проблема с Android, я создал небольшой тест с классом автомобиля, и это сработало. Благодаря! – jklmnn

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<Car>:

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(); 
0

Вы изобретаете колесо! Жизнь будет намного проще для вас, если вы используете шаблонный API коллекций. Для этого вы должны работать со списком вместо массивов, определить 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 в свой собственный класс и определить, какие атрибуты для сортировки в конструкторе.

Надежда, что помогает :)

Edit: Как уже отмечалось, этот подход также работает с массивами. Но если у вас нет оснований для работы с массивами, работать с коллекциями будет проще.

0

Я думаю, что решение было бы более эффективным, если бы вы передали реализацию Comparator в Array.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..... 
    } 
} 

, а затем просто отправить массив автомобилей в

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

, или использовать более специализированные классы компараторов, один для каждый атрибут и фабрику для их рендеринга, и, конечно же, не создавать новый Comparator() для каждого вида, если это происходит часто. :-)

В целом, этот подход должен сделать ваш код более эффективным. }

Смежные вопросы