Что делать с нулевыми полями в compare()?
В Java я использую класс, в котором некоторые поля могут быть null
. Например:
class Foo {
String bar;
//....
}
Я хочу написать BarComparator для этого класса,
private static class BarComparator
implements Comparator<Foo> {
public int compare( final Foo o1, final Foo o2 )
{
// Implementation goes here
}
}
Существует ли стандартный способ справиться с тем, что любой из o1
, o2
, o1.bar
, o2.bar
может быть null
, не записывая много вложенных if
... else
?
Ура!
Ответы
Ответ 1
Я думаю, вы могли бы обернуть вызов методу field compareTo с помощью небольшого статического метода для сортировки нулей высокого или низкого значения:
static <T extends Comparable<T>> int cp(T a, T b) {
return
a==null ?
(b==null ? 0 : Integer.MIN_VALUE) :
(b==null ? Integer.MAX_VALUE : a.compareTo(b));
}
Простое использование (несколько полей, как обычно):
public int compare( final Foo o1, final Foo o2 ) {
return cp(o1.field, o2.field);
}
Ответ 2
Спасибо за ответы! Общий метод и Google Comparators выглядят интересными.
И я обнаружил, что NullComparator в Apache Commons Коллекции (которые мы сейчас используем):
private static class BarComparator
implements Comparator<Foo>
{
public int compare( final Foo o1, final Foo o2 )
{
// o1.bar & o2.bar nulleness is taken care of by the NullComparator.
// Easy to extend to more fields.
return NULL_COMPARATOR.compare(o1.bar, o2.bar);
}
private final static NullComparator NULL_COMPARATOR =
new NullComparator(false);
}
Примечание. Я сосредоточился на поле bar
, чтобы сохранить его в точке.
Ответ 3
Это зависит от того, считаете ли вы, что нулевая запись является допустимым значением стоимости строки. имеет значение null < или > "яблоко". Единственное, что я мог сказать наверняка, это то, что null == null. Если вы можете определить, где null вписывается в порядок, вы можете написать код соответствующим образом.
В этом случае я могу выбрать исключение NullPointerExcpetion или IllegalArgumentException и попытаться обработать нуль на более высоком уровне, не поставив его в сравнение в первую очередь.
Ответ 4
Вы можете написать свой компаратор для этого. Допустим, у вас есть класс Person с именем String как частное поле. getName() и setName() для доступа к имени поля. Ниже приведен компаратор для класса Person.
Collections.sort(list, new Comparator<Person>() {
@Override
public int compare(Person a, Person b) {
if (a == null) {
if (b == null) {
return 0;
}
return -1;
} else if (b == null) {
return 1;
}
return a.getName().compareTo(b.getName());
}
});
Update:
С Java 8 вы можете использовать ниже API для списка.
// Push nulls at the end of List
Collections.sort(subjects1, Comparator.nullsLast(String::compareTo));
// Push nulls at the beginning of List
Collections.sort(subjects1, Comparator.nullsFirst(String::compareTo));
Ответ 5
Ключевым моментом здесь является выработка того, как вы хотите, чтобы нули обрабатывались. Некоторые варианты: а) предполагают, что нули попадают ко всем другим объектам в порядке сортировки; б) предполагают, что нули появляются после всех остальных объектов в порядке сортировки; в) обрабатывают нуль как эквивалент некоторого значения по умолчанию; d) обрабатывают нули как условия ошибки. Какой из них вы выберете, полностью зависит от приложения, над которым вы работаете.
В последнем случае, конечно, вы делаете исключение. Для остальных вам нужен четырехсторонний случай if/else (около трех минут кодирования, который вы разработали, какими должны быть результаты).
Ответ 6
Если вы используете коллекцию Google, вы можете найти класс Comparators. Если есть вспомогательные методы для упорядочения нулей в качестве либо наибольших, либо наименьших элементов в коллекции. Вы можете использовать компараторы компаса, чтобы уменьшить количество кода.
Ответ 7
Существует также класс org.springframework.util.comparator.NullSafeComparator
в Spring Framework, который вы можете использовать.
Пример (Java 8):
SortedSet<Foo> foos = new TreeSet<>( ( o1, o2 ) -> {
return new NullSafeComparator<>( String::compareTo, true ).compare( o1.getBar(), o2.getBar() );
} );
foos.add( new Foo(null) );
foos.add( new Foo("zzz") );
foos.add( new Foo("aaa") );
foos.stream().forEach( System.out::println );
Это напечатает:
Foo{bar='null'}
Foo{bar='aaa'}
Foo{bar='zzz'}
Ответ 8
Мне кажется, что нет способа сделать это, но в любом случае код не так длинный.
Ответ 9
Вы не должны использовать NullComparator так, как вы это делаете, - вы создаете новый экземпляр класса для каждой операции сравнения, а если, например, вы сортируете список из 1000 записей, что будет 1000 * log2 (1000) объектов, которые являются совершенно лишними. Это может быстро стать проблематичным.
Подкласс или его делегировать или просто реализовать свою собственную нулевую проверку - это действительно не так сложно:
private static class BarComparator
implements Comparator<Foo> {
private NullComparator delegate = new NullComparator(false);
public int compare( final Foo o1, final Foo o2 )
{
return delegate.compare(o1.bar, o2.bar);
}
}
Ответ 10
Я думаю, что предыдущие заявления о возврате были бы другой альтернативой многим ifs
например.
if(o1==null) return x;
if(o2==null) return x;
if(o1.getBar()==null) return x;
if(o2.getBar()==null) return x;
// No null checks needed from this point.
Ответ 11
Учитывая клиента как POJO.Мой ответ будет:
Comparator<Customer> compareCustomer = Comparator.nullsLast((c1,c2) -> c1.getCustomerId().compareTo(c2.getCustomerId()));
или
Comparator<Customer> compareByName = Comparator.comparing(Customer::getName,nullsLast(String::compareTo));