Java: Как обойти отсутствие Equatable интерфейса? - PullRequest
3 голосов
/ 18 июня 2010

Насколько я знаю, такие вещи, как SortedMap или SortedSet, используют compareTo (а не equals) на Comparable<?> типах для проверки равенства (contains, containsKey).

Но что, если определенные типы сопоставимы по концепции, но не сопоставимы?
(хэш-коды, адреса памяти, ...)

Я должен объявить Comparator<?> и переопределить метод int compareTo(T o1, To2). ОК, я могу вернуть 0 для экземпляров, которые считаются равными. Но, для необычных случаев, что я возвращаю, когда заказ не очевиден?

Является ли подход использования SortedMap или SortedSet на равным , но (по концепции) несопоставимых типов в любом случае хорош?

Спасибо!

EDIT:
Я не хочу хранить отсортированные вещи, но если бы я использовал «обычные» Map и Set, я бы не смог «переопределить» поведение равенства.

РЕДАКТИРОВАТЬ 2:
Почему я не могу просто переопределить equals(...):
Мне нужно изменить поведение равенства иностранного класса. Я не могу его редактировать.

РЕДАКТИРОВАТЬ 3:
Подумайте только о .NET: у них есть интерфейс IEquatable, который может изменять поведение равенства, не затрагивая сопоставимое поведение.

РЕДАКТИРОВАТЬ 4:
Разве я не могу просто заставить compareTo вернуть 0 для равных и 1 для неравных экземпляров? В чем большая проблема? У меня есть несколько тестов, кажется, что SortedMap / SortedSet вызывают CompareTo для пары экземпляров один раз. Да, порядок не имеет смысла, но почему это должно быть моей проблемой? Мне не нужен заказ. * Мне просто нужно изменить поведение равенства. К сожалению, большинство людей просто не могут этого понять.
ПРИМЕЧАНИЕ: Концепция возврата 1 для неравных случаев теперь оказалась неверной.

РЕДАКТИРОВАТЬ 5:
Изменение поведения равенства иностранных классов - это плохая концепция? Конечно? Я так не думаю: почему тогда мне разрешено изменять поведение сравнения иностранных классов , используя Comparator?

РЕДАКТИРОВАТЬ 6:
Спасибо Mark Peters и waxwing за идею обертывания типа ключа в пользовательский класс. Таким образом, я могу переопределить equals и hashCode, тем самым изменив поведение равенства.

Ответы [ 13 ]

10 голосов
/ 18 июня 2010

Попробуйте вместо этого обернуть свой иностранный класс в свой собственный.

public class Foreign {
  // undesired equals() and hashCode() implementation
}


public class ForeignWrapper {
   private Foreign foreign;

   public ForeignWrapper(Foreign foreign) {
      this.foreign = foreign;
   }

   public void equals() {
       // your equals implementation, using fields from foreign
   }

   public int hashCode() {
       // your hashCode implementation, using fields from foreign
   }

}

Затем добавьте new ForeignWrapper(foreign) к стандартному HashSet / HashMap. Не применимо во всех ситуациях, но возможно в вашей.

10 голосов
/ 18 июня 2010

Нет, использование SortedMap или SortedSet для одинаковых, но не сопоставимых типов - ужасная идея.Если они не сопоставимы в действительности или с помощью компаратора, их не следует использовать в SortedSet.Сортировка подразумевает, что есть порядок, то есть вы можете сравнить два элемента, чтобы увидеть, что «меньше».

Просто используйте HashMap / Set.

Отредактируйте к своему Edit # 2

Если вы не можете правильно переопределить equals, где-то выполучил очень плохой дизайн.Вам нужно будет дать больше информации о том, что вы пытаетесь выполнить.

Изменить до своего Редактировать # 3

В Java изменение, равное , не дает изменить сопоставимое поведение.Для этого вам не нужен интерфейс.

Отредактируйте в своем правке # 4

НЕТ, ВЫ НЕ МОЖЕТЕ ВЕРНУТЬСЯ 1 ДЛЯ НЕРАВНОВНЫХ ЭЛЕМЕНТОВ!!

SortedSets использует сравнение с find вашего элемента в наборе.Сопоставимый интерфейс имеет особые требования.То, что вы нарушаете, это то, что если A.compareTo(B) > 0, то обязательно B.compareTo(A) < 0.Вы нарушаете то, что делает невозможным поиск элементов в вашем наборе послесловий.

public static void main(String[] args) throws Exception {
    SortedSet<MyClass> set = new TreeSet<MyClass>();
    MyClass one = new MyClass(1);
    set.add(one);
    set.add(new MyClass(2));
    set.add(new MyClass(3));
    System.out.println(set.contains(one));
}
private static class MyClass implements Comparable<MyClass> {
    private final int data;
    private MyClass(int data) { this.data = data; }
    public int compareTo(MyClass o) { return (data == o.data ? 0 : 1); }
}

Этот код печатает false, поэтому, очевидно, ваш компаратор нарушил семантику набора.

6 голосов
/ 18 июня 2010

Похоже, вы не хотите / не нужно сортировать элементы.

В таком случае, может быть, вы можете использовать HashMap и HashSet вместо этого?Нет смысла использовать SortedMap и SortedSet, если вам не нужно их сортировать.

1 голос
/ 18 июня 2010

Если память не является большой проблемой, подкласс HashMap и HashSet принимают класс Equality

interface Equality<T>//Defines the equality behavior
{
   int hashCode(T t);//Required, always make sure equals = true => same hashCode
   boolean areEqual(T t,Object t2);
}
class EqualWrapper<T>//Wraps object and equality for the HashMap/Set
{
   T object;
   Equality<T> equal;
   int hashCode(){return equal.hashCode(object);}
   boolean equals(Object o){return equal.areEqual(object,o);}

}
class MySet<T>extends AbstractSet<T>
{
   private HashSet<EqualWrapper<T> > internalSet = new HashSet<T>();
   private Equality<T> equal;
   public MySet(Equality<T> et){equal = et;}
   // TODO implement abstract functions to wrapp 
   // objects and forward them to 
   // internalSet  
}

Таким образом, вы можете определить собственное поведение равенства.Странно, что он отсутствует в JRE

1 голос
/ 18 июня 2010

Это не ясно, но, возможно, все, что вы пытаетесь сделать, это получить коллекцию что-то , используя ту же семантику Set / Map, но с что-то которые не реализуют адекватно Object.equals.

В этом случае я предлагаю вам подкласс AbstractSet или AbstractMap и переопределить AbstractCollection.contains, чтобы использовать вашу версию equals.

Это не то, что я бы порекомендовал, но ваш вопрос на самом деле не проясняет, чего вы пытаетесь достичь.

См. http://java.sun.com/javase/6/docs/api/java/util/AbstractSet.html и http://java.sun.com/javase/6/docs/api/java/util/AbstractCollection.html#contains(java.lang.Object)

1 голос
/ 18 июня 2010

Если вам нужно переопределить hashCode, но вы не можете, я думаю, вы хотите расширить HashMap или написать свой собственный.

1 голос
/ 18 июня 2010

Является ли подход с использованием SortedMap или SortedSet на равных, но (по понятие) несопоставимые типы хорошие в любом случае?

Нет. Смысл этих коллекций в том, чтобы позволить вам сортировать объекты в них, если объекты не имеют естественного порядка сортировки, тогда какой смысл помещать их в отсортированную коллекцию?

Вы должны переопределить методы equals () и hashcode () и использовать вместо этого стандартные классы Map / Set.

1 голос
/ 18 июня 2010

Я не хочу хранить сортированные вещи, но если бы я использовал «обычную» Map и Set, я бы не смог «переопределить» поведение равенства.

Если выне хотите хранить отсортированные элементы, тогда почему вы используете отсортированную коллекцию?

Чтобы сохранить отсортированную коллекцию, операция вставки (обычно) имеет сложность O (log n) для размещения элементав правильном месте.Если вам не нужна сортировка, то это расточительно, так как вы можете использовать коллекцию на основе хеша (HashMap, HashSet), которая даст вам O (1) время вставки.

0 голосов
/ 18 июня 2010

Я не хочу хранить отсортированные вещи, но если бы я использовал «обычную» Map и Set, я бы не смог «переопределить» поведение равенства.

Выпытается переопределить метод equals () неизвестного типа.Вы думаете о проблеме, желающей иметь интерфейс IEquatable.Если вы должны использовать SortedSet / SortedMap, тогда предоставьте Comparator, такой как @ ptomli, упоминаемый в его ответе .

Использование HashMap / HashSet вместо этого, кажется хорошим предложением.Вы смотрели на них?

0 голосов
/ 18 июня 2010

Я не уверен, что вижу вашу точку зрения (я думаю, что вы пытаетесь решить проблему с неправильной стороны), но если вы все-таки просто хотите Set или Map, который поддерживает порядок ввода , затем используйте LinkedHashSet или LinkedHashMap соответственно.


Обновление : согласно вашей цитате:

Мне нужно изменить поведение равенства иностранного класса.Я не могу его отредактировать.

Внутри отсортированного набора / карты?Затем используйте TreeSet или TreeMap, которые вы создаете с помощью пользовательского Comparator.Например,

SortedSet<String> set = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);

(что составляет набор String с, упорядоченный в нечувствительном к регистру порядке).

См. Также:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...