Что делать с пустыми полями в 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?

Ваше здоровье!

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
22
0
24 301
10
Перейти к ответу Данный вопрос помечен как решенный

Ответы 10

Я думаю, что ранние операторы возврата были бы другой альтернативой множеству if.

например

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.

Это зависит от того, считаете ли вы пустую запись допустимым строковым значением, достойным сравнения. равно null <или> "яблоко". Единственное, что я могу сказать наверняка, это то, что null == null. Если вы можете определить, где null вписывается в порядок, вы можете написать код соответствующим образом.

В этом случае я мог бы выбрать исключение NullPointerExcpetion или IllegalArgumentException и попытаться обработать значение null на более высоком уровне, не помещая его в сравнение в первую очередь.

Мне нравится этот ответ. Спасибо!

Burkhard 27.03.2013 16:58
Ответ принят как подходящий

Я думаю, вы могли бы обернуть вызов метода поля 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);
}

Не по теме я знаю, но в чем причина предпочесть MIN / MAX_VALUE, а не - / + 1?

Steve Jessop 24.09.2008 21:03

Приносим извинения за задержку ответа. Это необходимо для того, чтобы у нас было неравенство треугольника. Для a> b> c, a.compareTo (b) + b.compareTo (c) <= a.compareTo (c). Не то чтобы никого это волновало ...

Tom Hawtin - tackline 11.10.2008 05:00

По-прежнему может выдать NullPointerException, если o1 / o2 равно нулю. Как следует относиться к нулевым значениям o1 / o2? Упомянутый OP: o1, o2, o1.bar, o2.bar может быть нулевым. Или это часть контракта компаратора: сравнение нулей должно вызывать NPE?

Daniel 18.06.2014 10:43

@Daniel Comparator.compare может генерировать NPE с аргументами null. Как правило, лучше всего как можно раньше отлавливать любые ложные нули или любую другую форму недопустимого аргумента (хотя я отмечаю, что в исходном вопросе упоминается o1, а o2 может быть null).

Tom Hawtin - tackline 18.06.2014 17:38

Ключевым моментом здесь является определение того, как вы хотите, чтобы значения NULL обрабатывались. Некоторые варианты: a) предполагать, что нули идут раньше всех других объектов в порядке сортировки; b) предполагать, что нули идут после всех других объектов в порядке сортировки; c) рассматривать null как эквивалент некоторого значения по умолчанию; d) рассматривать нули как условия ошибки. Какой из них вы выберете, полностью зависит от приложения, над которым вы работаете.

В последнем случае вы, конечно, генерируете исключение. Для других вам нужен четырехсторонний случай if / else (примерно за три минуты кодирования вы уже выработали, какими должны быть результаты).

Если вы используете коллекции Google, вам может быть полезен класс Компараторы. Если имеет вспомогательные методы для упорядочивания нулей как наибольших или наименьших элементов в коллекции. Вы можете использовать составные компараторы, чтобы уменьшить количество кода.

Спасибо за ответы! Общий метод и компараторы Google выглядят интересно.

И я обнаружил, что в Коллекции Apache Commons (который мы сейчас используем) есть NullComparator:

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, чтобы не упустить его.

Похоже, ссылка на Javadocs мертва. Теперь он находится по адресу commons.apache.org/proper/commons-collections/javadocs/….

JBert 06.02.2014 15:49

Вы не должны использовать 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);
    }
}

Вы правы, NullComparator должен быть частным статическим полем. Я написал это таким образом в примере, чтобы сосредоточиться на ничтожности.

Sébastien RoccaSerra 26.09.2008 00:50

Вы можете написать для него свой компаратор. Допустим, у вас есть класс Person со строковым именем в качестве частного поля. 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());
        }
    });

Обновлять:

Начиная с 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));

В Spring Framework также есть класс org.springframework.util.comparator.NullSafeComparator, который вы можете использовать.

Пример (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'}

Рассматривая Клиента как POJO, я бы ответил:

Comparator<Customer> compareCustomer = Comparator.nullsLast((c1,c2) -> c1.getCustomerId().compareTo(c2.getCustomerId()));

Или же

Comparator<Customer> compareByName = Comparator.comparing(Customer::getName,nullsLast(String::compareTo));

Другие вопросы по теме