Отрезать логику в методах, когда предусмотрен пользовательский Comparator или элементы реализуют Comparable?

1

Какова наилучшая практика разделения логики в методах, которые будут работать с элементами Comparable, или конкретный Компаратор предоставляется во время создания, в зависимости от того, какой конструктор класса используется? Пример:

public class MinMax<E> {

private final Comparator<? super E> comparator;
private final boolean isSpecificComparatorProvided;

public MinMax() {
    this.comparator = null;
    this.isSpecificComparatorProvided = false;
    ........;
}

public MinMax(Comparator<? super E> comparator) {
    this.comparator = comparator;
    this.isSpecificComparatorProvided = true;
    ........;
}

private boolean isBigOrEqual(E e1, E e2) {
    if (isSpecificComparatorProvided) {
        Comparator<? super E> cpr = comparator;
        int cmpr = cpr.compare(e1, e2);
        return cmpr > -1;
    } else {
        @SuppressWarnings("unchecked")
        Comparable<? super E> e1Comparable = (Comparable<? super E>) e1;
        int cmprTo = e1Comparable.compareTo(e2);
        return cmprTo > -1;
    }
}

}

Всегда ли проверяю конкретный компаратор в методе isBigOrEqual()? Кроме того, применяется литье к Comparable, когда имеется NO конкретный компаратор? Есть ли другой способ?

Теги:
collections
comparator
comparable

3 ответа

0
Лучший ответ

Вероятно, самый простой способ справиться с этим - только реализовать случай для Comparator, а затем, когда вы не передадите Comparator, напишите тот, который работает только на Comparable. (В Java 8 это просто Comparator.naturalOrder().)

new Comparator<T> {
  @Override public int compare(T a, T b) {
    return a.compareTo(b);
  }
};

TreeMap не делает этого по соображениям производительности, но проще это сделать.

0

Мне кажется, вы хотите абстрагировать способ сравнения двух объектов типа E и есть две реализации. Один использует конкретный Comparator, а другой - для объектов Comparable. Таким образом, вы можете разделить эти два поведения на отдельные классы (реализации).

Начнем с интерфейса для нашей абстрактной концепции:

interface AbstractComparer<E> {
    public boolean isBigOrEqual(E e1, E e2);
}

Извините за неназванное имя.

Затем давайте реализовать случай, который касается объектов Comparable:

class ComparableComparer<E extends Comparable<E>> implements AbstractComparer<E> {

    @Override
    public boolean isBigOrEqual(E e1, E e2) {
        return e1.compareTo(e2) > -1;
    }

}

Реализация для другого случая получает конкретный компаратор через конструктор:

class ComparatorComparer<E> implements AbstractComparer<E> {

    private Comparator<E> comparator;

    public ComparatorComparer(Comparator<E> comparator) {
        this.comparator = comparator;
    }

    @Override
    public boolean isBigOrEqual(E e1, E e2) {
        return this.comparator.compare(e1, e2) > -1;
    }
}

Теперь мы можем упростить класс MinMax и мы можем предоставить метод сравнения через конструктор:

public class MinMax<E> {

    private final AbstractComparer<E> comparer;

    public MinMax(AbstractComparer<E> comparer) {
        this.comparer = comparer;
    }

    private boolean isBigOrEqual(E e1, E e2) {
        return comparer.isBigOrEqual(e1, e2);
    }
}
  • 0
    Очень хорошее решение. Я думал, что найду лучшую практику, глядя на исходный код хорошо известных классов из фреймворка Java, например TreeMap <K, V>, но я не могу понять это из-за большого размера кода. Для ключа K в TreeMap <K, V> ситуация такая же, как и в моем случае: два конструктора - один для использования естественного упорядочения ключей и все ключи, вставленные в карту, должны реализовывать интерфейс Comparable. И второй конструктор для случая, когда предусмотрен Comparator.
  • 0
    Это спорный вопрос, какой подход лучше. С моим подходом это яснее и безопаснее, потому что вы избегаете использования Comparable объектов без указанного Comparator . И вам не нужно везде свернуть свой код с comparator == null проверками. Но если TreeMap был реализован так, как я это сделал, то пользователю TreeMap всегда нужно будет предоставлять объект AbstractComparer, что будет довольно раздражающим. Таким образом, вам придется решить, какой подход лучше для ваших нужд.
0

Вы можете ограничить E сопоставимым с public class MinMax<E extends Comparable<E>> поэтому приведение не требуется больше, поскольку E во время компиляции E должно быть сопоставимым:

public class MinMax<E extends Comparable<E>> {

    private final Comparator<? super E> comparator;

    private final boolean isSpecificComparatorProvided;

    public MinMax() {
        this.comparator = null;
        this.isSpecificComparatorProvided = false;
    }

    public MinMax(Comparator<? super E> comparator) {
        this.comparator = comparator;
        this.isSpecificComparatorProvided = true;
    }

    private boolean isBigOrEqual(E e1, E e2) {
        if (this.isSpecificComparatorProvided) {
            return this.comparator.compare(e1, e2) > -1;
        } else {
            return e1.compareTo(e2) > -1;
        }
    }
}
  • 0
    Этот ответ кажется мне полезным. Я хочу спросить, насколько я понимаю, не будет проблем во время выполнения, если E не реализует Comparable, но конкретный Comparator предоставляется, верно?
  • 0
    Нет. Будет ошибка компиляции, если E не реализует Comparable.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню