2009-08-11 2 views
7

Я пишу некоторые пользовательские компараторы, и я бы хотел, чтобы они нажимали нулевые элементы в нижней части списка, независимо от того, сортирую ли я сортировку по возрастанию или по убыванию. Какая хорошая стратегия или образец для этого?Хороший общий способ сортировки нулей на дно, независимо?

Навскидку:

  • Просто напишите отдельные восходящие и убыванию компараторов, разделяя код , где это возможно
  • Делегат обращение нуль другому класса, либо путем выбрасывания NPE или , называя его явно
  • Включите восходящий флаг и поставьте условную логику в нем для навигации вокруг нулей
  • Wrap регулярные компараторов в классе нуль-обработки

Любые другие стратегии? Я хотел бы услышать о любых опытах с различными подходами и о любых подводных камнях для различных стратегий.

ответ

5

Последний вариант мне очень понравился. Компараторы действительно здоровы, чтобы объединиться. В частности, вы можете написать ReverseComparator, а также NullWrappingComparator.


EDIT: Вам не нужно писать это самостоятельно. Если вы посмотрите на Ordering класса в Google Collections Library вы найдете это и все виды других вкусностей :)


EDIT: Переход на более подробно, чтобы показать, что я имею в виду ReverseComparator ...

Одно из слов предупреждения - при реализации ReverseComparator отмените порядок аргументов вместо отрицания результата, так как иначе Integer.MIN_VALUE «обращен» к себе.

Так эта реализация не так (при условии, original является компаратор на обратный):

public int compare(T x, T y) 
{ 
    return -original.compare(x, y); 
} 

, но это правильно:

public int compare(T x, T y) 
{ 
    return original.compare(y, x); 
} 

Причина заключается в том, что мы всегда хотим обратить сравнение, но если original.compare(x, y) возвращает int.MIN_VALUE, то плохим компартером будет такжеint.MIN_VALUE, что неверно. Это связано с забавным свойством, которое int.MIN_VALUE == -int.MIN_VALUE.

+0

Я согласен со всем вашим ответом, кроме как отрицать результат сравнения; Я был бы довольно недоволен, если бы узнал, что компаратор реализует целочисленное сравнение с использованием вычитания, так как этот метод имеет так много подводных камней. – jprete

+1

@jprete: Я думаю, вы меня неправильно поняли. Я отредактирую. –

+1

Я принимаю этот ответ из-за ссылки на класс заказов Google Collections - существующий проверенный код - лучшее решение. Также для предупреждения о Integer.MIN_VALUE. Но я очень ценю код @ dfa, ниже, и хочу, чтобы я мог принять оба ответа. –

10

Я согласен с Джоном Скитом (это так просто :).Я пытался реализовать очень простой decorator:

class NullComparators { 

    static <T> Comparator<T> atEnd(final Comparator<T> comparator) { 
     return new Comparator<T>() { 

      public int compare(T o1, T o2) { 
       if (o1 == null && o2 == null) { 
        return 0; 
       } 

       if (o1 == null) { 
        return 1; 
       } 

       if (o2 == null) { 
        return -1; 
       } 

       return comparator.compare(o1, o2); 
      } 
     }; 
    } 

    static <T> Comparator<T> atBeginning(final Comparator<T> comparator) { 
     return Collections.reverseOrder(atEnd(comparator)); 
    } 
} 

дали Компаратор:

Comparator<String> wrapMe = new Comparator<String>() { 
     public int compare(String o1, String o2) { 
      return o1.compareTo(o2); 
     } 
}; 

и некоторые тестовые данные:

List<String> strings = Arrays.asList(null, "aaa", null, "bbb", "ccc", null); 

вы можете сортировать с нулями на конце:

Collections.sort(strings, NullComparators.atEnd(wrapMe)); 
 
[aaa, bbb, ccc, null, null, null] 

или в начале:

Collections.sort(strings, NullComparators.atBeginning(wrapMe)); 
 
[null, null, null, ccc, bbb, aaa] 
+3

Очень приятно! Спасибо. Есть ли причина для отказа 0, если оба аргумента равны нулю? Я знаю, что нулевое поведение не совсем похоже на непустое поведение, а утверждение о том, что два нуля равны, сомнительно, - но менее сомнительно сказать, что один превосходит другой? –

+2

@Carl: Именно то, что я только что сделал на ваш пост :) Компаратор * должен * вернуть 0 или выбросить исключение при передаче двух нулей, иначе он не подчиняется контракту интерфейса. –

5

Вслед за ответ ДКИ - то, что я хочу, что обнуляет роды в конце, не затрагивая порядок не-нулей. Так что я хочу что-то больше вдоль линий этого:

public class NullComparatorsTest extends TestCase { 
    Comparator<String> forward = new Comparator<String>() { 
            public int compare(String a, String b) { 
             return a.compareTo(b); 
            } 
           }; 

    public void testIt() throws Exception { 
     List<String> strings = Arrays.asList(null, "aaa", null, "bbb", "ccc", null); 
     Collections.sort(strings, NullComparators.atEnd(forward)); 
     assertEquals("[aaa, bbb, ccc, null, null, null]", strings.toString()); 
     Collections.sort(strings, NullComparators.atBeginning(forward)); 
     assertEquals("[null, null, null, aaa, bbb, ccc]", strings.toString()); 
    } 
} 

public class NullComparators { 
    public static <T> Comparator<T> atEnd(final Comparator<T> comparator) { 
     return new Comparator<T>() { 
      public int compare(T a, T b) { 
       if (a == null && b == null) 
        return 0; 
       if (a == null) 
        return 1; 
       if (b == null) 
        return -1; 
       return comparator.compare(a, b); 
      } 
     }; 
    } 

    public static <T> Comparator<T> atBeginning(final Comparator<T> comparator) { 
     return new Comparator<T>() { 
      public int compare(T a, T b) { 
       if (a == null && b == null) 
        return 0; 
       if (a == null) 
        return -1; 
       if (b == null) 
        return 1; 
       return comparator.compare(a, b); 
      } 
     }; 
    } 
} 

Полный кредит DFA, хотя - это лишь незначительная модификация его работы.

+1

Одна проблема: вы не возвращаете 0 при сравнении двух нулей. –

+0

Thanks; отредактирован с исправлением. –

2

Вы всегда можете использовать NullComparator из коллекций коллекций. Это было дольше, чем Google Collections.

3

В Java 8 вы можете использовать статические методы Comparator.nullsLast и Comparator.nullsFirst, чтобы иметь более нулеустойчивые компараторы. Предположим, у вас есть Fruit класс вроде следующего:

public class Fruit { 
    private final String name; 
    private final Integer size; 

    // Constructor and Getters 
} 

Если вы хотите отсортировать кучу фруктов по их размеру и положить null с на конце:

List<Fruit> fruits = asList(null, new Fruit("Orange", 25), new Fruit("Kiwi", 5)); 

Вы можете просто написать:

Collections.sort(fruits, Comparator.nullsLast(Comparator.comparingInt(Fruit::getSize))); 

И результат будет:

[Fruit{name='Kiwi', size=5}, Fruit{name='Orange', size=25}, null]