Что делать с пустыми полями в compare ()? - PullRequest
21 голосов
/ 24 сентября 2008

В 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 * * 1015

Ура!

Ответы [ 11 ]

36 голосов
/ 24 сентября 2008

Полагаю, вы могли бы обернуть вызов метода fieldTo с помощью небольшого статического метода для сортировки значений нуля по максимуму или минимуму:

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);
}
8 голосов
/ 25 сентября 2008

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

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

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, чтобы оно оставалось на месте.

6 голосов
/ 24 сентября 2008

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

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

3 голосов
/ 02 октября 2015

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

Обновление:

Начиная с Java 8, вы можете использовать ниже API для List.

// 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));
2 голосов
/ 28 октября 2015

Существует также класс 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'}
2 голосов
/ 24 сентября 2008

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

2 голосов
/ 24 сентября 2008

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

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

1 голос
/ 12 января 2018

Рассматривая клиента как POJO. Мой ответ будет:

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

Или

Comparator<Customer> compareByName = Comparator.comparing(Customer::getName,nullsLast(String::compareTo));
1 голос
/ 25 сентября 2008

Вы не должны использовать 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);
    }
}
1 голос
/ 24 сентября 2008

Мне кажется, нет способа сделать это, но в любом случае код не такой длинный.

...