Хороший общий способ сортировки нулей, независимо от? - PullRequest
8 голосов
/ 11 августа 2009

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

Вне руки:

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

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

Ответы [ 5 ]

11 голосов
/ 11 августа 2009

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

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]
6 голосов
/ 11 августа 2009

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


РЕДАКТИРОВАТЬ: Вам не нужно писать это самостоятельно. Если вы посмотрите на класс Ordering в Библиотеке Google Коллекции , вы найдете этот и другие полезные вещи:)


РЕДАКТИРОВАТЬ: углубляясь в подробности, чтобы показать, что я имею в виду 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.

5 голосов
/ 11 августа 2009

В ответ на ответ dfa - я хочу, чтобы нулевые значения сортировались в конце, не влияя на порядок ненулевых значений. Поэтому я хочу что-то еще в этом духе:

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, хотя - это всего лишь незначительная модификация его работы.

3 голосов
/ 08 августа 2016

В 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]
2 голосов
/ 12 августа 2009

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

...