#java #sorting #comparable
#java #сортировка #сопоставимый
Вопрос:
Я создаю простую программу, чтобы узнать о классе Java Comparator. Я отсортировал Arraylist
по порядку, но теперь я хочу отсортировать список в порядке убывания, но у меня возникли проблемы с тем, где вызвать .reverseOrder()
метод, поскольку я использовал внутренний класс, который реализует Comparator<Song>
(song — это класс song, в котором находятся методы получения и установки).
Вот мой SongSort
класс, в котором находится процесс сортировки и т.д.;
import java.util.*;
import java.io.*;
public class SongSort
{
ArrayList<Song> songList = new ArrayList<Song>();
public void main(String[] args)
{
new SongSort().go();
}
class ArtistCompare implements Comparator<Song>
{
public int compare(Song one, Song two)
{
return one.getRating().compareTo(two.getRating());
}
}
public void go()
{
getSongs();
System.out.println(songList);
//Collections.sort(songList);
System.out.println(songList);
ArtistCompare artistCompare = new ArtistCompare();
Collections.sort(songList, artistCompare);
System.out.println(songList);
}
public void getSongs()
{
try{
File file = new File("SongListMore.txt");
BufferedReader reader = new BufferedReader(new FileReader(file));
String line = null;
while((line = reader.readLine()) != null)
{
addSong(line);
}
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
public void addSong(String lineToParse)
{
String [] tokens = lineToParse.split("/");
Song nextSong = new Song(tokens[0], tokens[1], tokens[2], tokens[3]);
songList.add(nextSong);
}
}
И вот мой простой Song
класс;
public class Song //implements Comparable<Song>
{
private String title;
private String artist;
private String rating;
private String bpm;
public Song(String t, String a, String r, String b)
{
title = t;
artist = a;
rating = r;
bpm = b;
}
public String getTitle()
{
return title;
}
public String getArtist()
{
return artist;
}
public String getRating()
{
return rating;
}
public String getBpm()
{
return bpm;
}
public String toString()
{
return ("Title : " title "," " Artist : " artist " Rating : " rating);
}
}
Кто-нибудь может помочь мне выяснить, где я буду вызывать reverseOrder()
метод в SongSort
классе, поскольку он не будет компилироваться?
Ответ №1:
ArtistCompare artistCompare = new ArtistCompare();
Collections.sort(songList, Collections.reverseOrder(artistCompare));
Редактировать Июль 2015
Поскольку этот ответ все еще привлекает некоторое внимание, вот небольшое обновление:
С Java SE 8 становится проще создавать обратный компаратор:
Comparator<Song> songRatingComparator = Comparator.comparing(Song::getRating);
Collections.sort(songList, songRatingComparator.reversed());
И вы, конечно, также можете использовать Streams framework:
List<Song> sortedSongList = songList.stream()
.sorted(Comparator.comparing(Song::getRating).reversed())
.collect(Collectors.toList());
Комментарии:
1. Что, если я хочу создать компаратор по двум полям, по возрастанию двойного поля и по убыванию строкового поля?
2. @gstackoverflow Пожалуйста, взгляните на Javadoc. Существуют аналогичные методы для двойных свойств (comparingDouble, затем Comparingdouble), и есть перегруженные методы, в которые вы можете передавать другие компараторы, такие как ReverseOrder())
3. Вы можете сделать
songList.sort(Comparator.comparing(Song::getRating).reversed())
, нет необходимости передавать его в потоковом режиме (если исходный список не является неизменяемым)
Ответ №2:
Одним из способов реализации компаратора обратного порядка является реализация делегата-компаратора, который инвертирует результат компаратора (путем изменения порядка).
public class ReverseOrder<T> implements Comparator<T> {
private Comparator<T> delegate;
public ReverseOrder(Comparator<T> delegate){
this.delegate = delegate;
}
public int compare(T a, T b) {
//reverse order of a and b!!!
return this.delegate.compare(b,a);
}
}
Итак, единственное, что вам нужно сделать, это использовать этот делегат.
Например:
Comparator myComparator = new myComparator();
List list = ...;
List reverse = new ArrayList(list);
//acceding
Collections.sort(list, myComparator);
//descending
Collections.sort(list, new ReverseOrder(myComparator));
Комментарии:
1. java.utils. Collections.ReverseOrder(Comparator c) сделает то же самое и инвертирует сравнение вашего компаратора. Поэтому вам не следует изобретать велосипед здесь, а использовать Java SE API.
2. Вот почему ответ Puce является принятым ответом. — В любом случае я не буду удалять этот ответ, потому что он является действительным.
3. Мне нравится этот ответ, потому что он более надежный, а компараторы имеют больше применений, чем сортировка существующих списков. Например, я смог использовать это в конструкции TreeMap (Comparator), которая является SortedMap.
Ответ №3:
Давайте возьмем простой пример, у нас есть класс Person с двумя полями name age, и мы хотим отсортировать существующую коллекцию людей на основе их возраста, поэтому давайте предположим, что у нас есть класс Person с конструктором и добавим людей в список, а затем отсортируем их, не используя метод sort of collection :
Person bachiri = new Person (17,"bachiri");
Person taoufiq = new Person (14,"Taoufiq");
Person abderrahman = new Person (15,"abderrahman");
List<Person> persons = new ArrayList<>();
и это это побуждение Agecomparable :
class AgeComparator implements Comparator<Person>{
@Override
public int compare(Person person1, Person person2) {
return Integer.compare(person1.getAge(),person2.getAge());
}
}
хитрость заключается в том, чтобы умножить возвращаемый метод на -1, чтобы конечный результат был обратным:
класс AgeComparator реализует компаратор{
@Override
public int compare(Person person1, Person person2) {
return -1 * Integer.compare(person1.getAge(),person2.getAge());
}
}
итак, теперь мы можем получить обратный результат :
Collection.sort (Persons, new AgeComparator());
Комментарии:
1. Я только что видел ответ @Ralph, что практично, вы можете переключать параметры a, b восьмеркой или писать
return -1 *this.delegate.compare(a,b);
2.небольшая настройка
@Override public int compare(Person person1, Person person2) { if (person1.getAge() == null || person2.getAge == null) { return 1; } return Integer.compare(person2.getAge(),person1.getAge()); }
Ответ №4:
Если вам нужно использовать компаратор, который изменяет текущий порядок, просто верните отрицательное значение в compare
методе.
public class ComparatorInverse implements Comparator<Object> {
@Override
public int compare(Object lhs, Object rhs) {
return -1;
}
}
Комментарии:
1. Это неверно. Взгляните на Javadoc: docs.oracle.com/javase/7/docs/api/java/util /…
2. Возврат фиксированного значения, независимого от аргументов, вообще не имеет никакого смысла, не говоря уже о случае обратного порядка. Просто поменяйте местами аргументы, и теперь они упорядочены по-другому — это вообще не приведет к какому-либо стабильному порядку.
3. Я вижу, что я не сохраняю ограничения. Даже если это не имеет смысла, у меня это работает.
4. Я предполагаю, что в вашем коде вы сначала вставляете объекты в отсортированном порядке, в чем-то вроде ArrayList, который сохраняет порядок вставки. Теперь вызов sort в этом списке с помощью
ComparatorInverse
будет вызыватьcompare()
фиксированным и предсказуемым образом всегдаlhs < rhs
. Затем возврат -1 изменяет этот порядок. Но если вы вставляете свои элементы (скажем, числа) случайным образом или алгоритм сортировки изменяется, это больше не приведет к правильному результату с точки зрения естественного способа сортировки чего бы то ни былоObject
.5. Да, элементы в порядке — в моем решении я написал, чтобы изменить текущий порядок. Метод Collections.sort() вызовет сортировку слиянием, которая переключает все элементы в сортировке (возвращая значение -1). И, конечно, вы правы, если они изменят алгоритм сортировки, это может все испортить 🙂 — так что это неверно.