Я реализую compareTo()
метод для простого класса, такого как этот (чтобы можно было использовать Collections.sort()
и другие полезности, предлагаемые платформой Java):
public class Metadata implements Comparable<Metadata> {
private String name;
private String value;
// Imagine basic constructor and accessors here
// Irrelevant parts omitted
}
Я хочу, чтобы естественный порядок этих объектов был: 1) отсортирован по имени и 2) отсортирован по значению, если имя совпадает; оба сравнения должны быть без учета регистра. Для обоих полей нулевые значения вполне приемлемы, поэтому compareTo
не должны ломаться в этих случаях.
Решение, которое приходит на ум, заключается в следующем (я использую «охранные предложения» здесь, в то время как другие могут предпочесть одну точку возврата, но это не относится к делу):
// primarily by name, secondarily by value; null-safe; case-insensitive
public int compareTo(Metadata other) {
if (this.name == null && other.name != null){
return -1;
}
else if (this.name != null && other.name == null){
return 1;
}
else if (this.name != null && other.name != null) {
int result = this.name.compareToIgnoreCase(other.name);
if (result != 0){
return result;
}
}
if (this.value == null) {
return other.value == null ? 0 : -1;
}
if (other.value == null){
return 1;
}
return this.value.compareToIgnoreCase(other.value);
}
Это делает работу, но я не совсем доволен этим кодом. По общему признанию это не очень сложно, но довольно многословно и утомительно.
Вопрос в том, как бы вы сделали это менее многословным? (при сохранении функциональности)? Не стесняйтесь обращаться к стандартным библиотекам Java или Apache Commons, если они помогают. Будет ли единственный вариант сделать это (немного) проще - реализовать мой собственный "NullSafeStringComparator" и применить его для сравнения обоих полей?
Редактирует 1-3 : Эдди прав; исправлен вышеупомянутый случай "оба имени нулевые"
О принятом ответе
Я задал этот вопрос еще в 2009 году, конечно, на Java 1.6 и в то время, когда Эдди разработал чистое решение JDK. было моим предпочтительным принятым ответом. Я никогда не удосужился изменить это до сих пор (2017).
Есть также сторонние библиотечные решения - Apache Commons Collections один 2009 года и Guava 2013 года, оба опубликованные мной, - которые я предпочел в какой-то момент времени.
Я теперь сделал чистое решение Java 8 от Лукаша Виктора принятым ответом. Это определенно должно быть предпочтительным, если на Java 8, и в настоящее время Java 8 должна быть доступна почти для всех проектов.
Ответы:
Используя Java 8 :
источник
Collections.sort(List)
он не работает, когда список содержит нули, комментарий не имеет отношения к вопросу.Вы можете просто использовать Apache Commons Lang :
источник
nullsFirst()
/nullsLast()
.org.apache.commons.lang3
) является «устаревшим / плохо обслуживаемым / некачественным», является ложным или в лучшем случае необоснованным. Commons Lang3 легко понять и использовать, и он активно поддерживается. Вероятно, это моя наиболее часто используемая библиотека (кроме Spring Framework и Spring Security) - класс StringUtils с его нулевыми безопасными методами, например, делает нормализацию ввода тривиальной.Я бы реализовал нулевой безопасный компаратор. Там может быть реализация, но это так просто реализовать, что я всегда катал свою собственную.
Примечание: ваш компаратор выше, если оба имени имеют нулевое значение, даже не сравнивает поля значений. Я не думаю, что это то, что вы хотите.
Я бы реализовал это следующим образом:
РЕДАКТИРОВАТЬ: Исправлены опечатки в примере кода. Вот что я получаю за то, что не проверил это первым!
РЕДАКТИРОВАТЬ: Повышен nullSafeStringComparator в статический.
источник
final
ключевое слово не является действительно необходимым (Java-код уже многословен как есть.) Однако он предотвращает повторное использование параметров в качестве локальных переменных (ужасная практика кодирования). наше коллективное понимание программного обеспечения со временем улучшается, мы знаем, что по умолчанию все должно быть окончательным / постоянным / неизменным. Поэтому я предпочитаю получить немного больше многословия при использованииfinal
в объявлениях параметров (как бы тривиально ни была эта функция), чтобы получить ееinmutability-by-quasi-default
.) Затраты на ее усвояемость / ремонтопригодность незначительны в общей схеме вещей.Смотрите в нижней части этого ответа для обновленного (2013) решения с использованием Guava.
Это то, что я в конечном итоге пошел с. Оказалось, что у нас уже есть служебный метод для сравнения нулевых строк, поэтому самым простым решением было использовать его. (Это большая кодовая база; легко пропустить такую вещь :)
Вот как определяется помощник (он перегружен, так что вы также можете определить, будут ли значения NULL первыми или последними, если хотите):
Так что это по сути то же самое, что и ответ Эдди (хотя я бы не назвал метод статического помощника компаратором ) и ответ Ужина .
Во всяком случае, в целом, я бы сильно предпочел решение Патрика , так как считаю, что хорошей практикой является использование по возможности существующих библиотек. ( Знайте и используйте библиотеки, как говорит Джош Блох.) Но в этом случае это не дало бы самый чистый и простой код.
Редактировать (2009): версия Apache Commons Collections
Собственно, вот способ упростить решение на основе Apache Commons
NullComparator
. Объедините это с регистраComparator
вString
классе:Теперь это довольно элегантно, я думаю. (Остается только одна небольшая проблема: Commons
NullComparator
не поддерживает генерики, поэтому есть непроверенное назначение.)Обновление (2013): версия Guava
Спустя почти 5 лет, вот как я решил свой первоначальный вопрос. Если бы я писал на Java, я бы (конечно) использовал Guava . (И, конечно, не Apache Commons.)
Поместите эту константу куда-нибудь, например, в класс "StringUtils":
Затем в
public class Metadata implements Comparable<Metadata>
:Конечно, это почти идентично версии Apache Commons (обе используют JDK CASE_INSENSITIVE_ORDER ), использование
nullsLast()
единственной специфической для Guava вещи. Эта версия предпочтительна просто потому, что Guava предпочтительнее, чем зависимость, от коллекций Commons. (Как все согласны .)Если вам интересно
Ordering
, обратите внимание, что он реализуетComparator
. Это очень удобно, особенно для более сложных задач сортировки, позволяя, например, объединить несколько заказов с помощьюcompound()
. Читайте объяснение заказа для более!источник
ComparatorChain
вам не нужен собственныйcompareTo
метод.Я всегда рекомендую использовать Apache Commons, так как он, скорее всего, будет лучше, чем тот, который вы можете написать самостоятельно. Кроме того, вы можете выполнять «настоящую» работу, а не заново изобретать.
Интересующий вас класс - это нулевой компаратор . Это позволяет вам делать нули на высоком или низком уровне. Вы также даете ему свой собственный компаратор для использования, когда два значения не равны нулю.
В вашем случае вы можете иметь статическую переменную-член, которая выполняет сравнение, а затем ваш
compareTo
метод просто ссылается на это.Что-то вроде
}
Даже если вы решите свернуть свой собственный, помните об этом классе, так как он очень полезен при упорядочении списков, которые содержат нулевые элементы.
источник
Я знаю, что это может быть не прямой ответ на ваш вопрос, потому что вы сказали, что нулевые значения должны поддерживаться.
Но я просто хочу отметить, что поддержка пустых значений в CompareTo не соответствует контракту CompareTo, описанному в официальных документах Javadoc для Comparable :
Так что я бы либо выдал NullPointerException явно, либо просто позволил бы его выбрасывать в первый раз, когда нулевой аргумент разыменовывается.
источник
Вы можете извлечь метод:
}
источник
Вы можете сделать свой класс неизменным (Effective Java 2nd Ed. Имеет большой раздел по этому вопросу, пункт 15: Минимизируйте изменчивость) и убедитесь, что при построении невозможны нулевые значения (и при необходимости используйте шаблон нулевых объектов ). Затем вы можете пропустить все эти проверки и смело предположить, что значения не равны нулю.
источник
Я искал что-то подобное, и это казалось немного сложным, поэтому я сделал это. Я думаю, что это немного легче понять. Вы можете использовать его как компаратор или как один вкладыш. Для этого вопроса вы бы изменили на CompareToIgnoreCase (). Как есть, нули всплывают. Вы можете перевернуть 1, -1, если хотите, чтобы они опустились.
,
источник
мы можем использовать Java 8, чтобы сделать нулевое сравнение объекта. Предполагается, что у меня есть класс Boy с 2 полями: имя строки и целочисленный возраст, и я хочу сначала сравнить имена, а затем возраст, если оба они равны.
и результат:
источник
В случае, если кто-то использует Spring, есть класс org.springframework.util.comparator.NullSafeComparator, который делает это и для вас. Просто украсьте свой собственный сопоставимый с ним, как это
new NullSafeComparator<YourObject>(new YourComparable(), true)
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/comparator/NullSafeComparator.html
источник
Для конкретного случая, когда вы знаете, что данные не будут иметь нулевых значений (это всегда хорошая идея для строк), а данные действительно большие, вы все равно проводите три сравнения перед тем, как сравнивать значения, если вы точно знаете, что это ваш случай , Вы можете оптимизировать немного. YMMV как читаемый код превосходит незначительную оптимизацию:
источник
вывод
источник
Одним из простых способов использования NullSafe Comparator является использование его реализации в Spring, ниже приведен один из простых примеров для ссылки:
источник
Еще один пример Apache ObjectUtils. Умеет сортировать другие типы объектов.
источник
Это моя реализация, которую я использую для сортировки моего ArrayList. нулевые классы сортируются до последнего.
для моего случая EntityPhone расширяет EntityAbstract, а мой контейнер - List <EntityAbstract>.
метод «CompareIfNull ()» используется для безопасной сортировки нуля. Другие методы приведены для полноты, и показывают, как можно использовать compareIfNull.
источник
Если вы хотите простой Hack:
если вы хотите поставить нулевые значения в конец списка, просто измените этот метод выше
источник