3

据我所知,诸如SortedMapor之类的东西在类型上SortedSet使用compareTo(而不是equalsComparable<?>来检查相等性(contains, containsKey)。

但是,如果某些类型在概念上是相等的,但不可比较怎么办?
(哈希码、内存地址……)

我必须声明一个Comparator<?>并覆盖方法int compareTo(T o1, To2)。好的,对于被认为相等的实例,我可以返回 0。但是,对于特殊情况,如果订单不明显,我应该返回什么?

在equatable但(从概念上)不可比较的类型上使用 SortedMap 或 SortedSet 的方法是否很好?

谢谢!

编辑:
我不想存储排序的东西,但我会使用“通常”的 Map 和 Set,我不能“覆盖”平等行为。

编辑 2:
为什么我不能直接覆盖equals(...)
我需要改变一个外部类的平等行为。我无法编辑它。

编辑 3:
想想 .NET:他们有 IEquatable 接口,它可以改变相等行为而不触及可比较的行为。

编辑4:
我不能只compareTo为相等的实例返回0,为不相等的实例返回1吗?有什么大问题?我已经完成了一些测试,似乎 SortedMap/SortedSet 在一对实例上调用了 compareTo 一次。是的,订单没有意义,但为什么会是我的问题呢?我不需要订单。*我只需要改变平等行为。可悲的是,大多数人无法理解这一点。
注意:现在证明不相等实例返回 1 的概念是错误的。

编辑5:改变外国班级
的 平等行为是一个坏概念?当然?我不这么认为:为什么我可以使用 改变外国班级的比较行为?Comparator

编辑 6:
感谢Mark Peters并感谢waxwing将密钥类型包装在自定义类中的想法。这样,我可以覆盖 equals 和 hashCode,从而改变相等行为。

4

13 回答 13

10

不,在相等但不可比较的类型上使用 SortedMap 或 SortedSet 是一个可怕的想法。如果它们不能在本质上或通过比较器进行比较,则不应在 SortedSet 中使用它们。排序意味着有排序,这意味着您可以比较两个项目以查看哪个“更少”。

只需使用 HashMap/Set。

编辑您的编辑#2

如果你不能正确地覆盖equals,那么你的设计就很糟糕。您需要提供更多信息来了解您要完成的工作。

编辑您的编辑#3

在 Java 中,修改 equals不会改变可比较的行为。您不需要界面来完成此操作。

编辑到你的编辑#4

不,您不能只为不相等的元素返回 1!

SortedSets 使用比较来查找集合中的元素。可比较的接口有特定的要求。你要打破的是,如果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,因此很明显您的比较器已经破坏了 Set 的语义。

于 2010-06-18T13:20:09.867 回答
10

考虑将您的外国课程包装在您自己的课程中。

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。并非适用于所有情况,但可能适用于您的情况。

于 2010-06-18T14:19:05.140 回答
6

看起来您不想/不需要对元素进行排序。

在这种情况下,也许您可​​以使用HashMapandHashSet来代替?使用没有意义,SortedMap如果SortedSet您不需要对其进行排序。

于 2010-06-18T13:20:11.697 回答
1

我不想存储排序的东西,但我会使用“通常”的 Map 和 Set,我不能“覆盖”平等行为。

如果您不想存储已排序的元素,那么为什么要使用已排序的集合?

为了维护有序集合,插入操作(通常)具有 O(log n) 复杂度以将元素放置在正确的位置。如果您不需要排序,那么这很浪费,因为您可以使用基于哈希的集合(HashMap、HashSet),这会给您 O(1) 的插入时间。

于 2010-06-18T13:21:57.980 回答
1

在 equatable 但(从概念上)不可比较的类型上使用 SortedMap 或 SortedSet 的方法是否很好?

不。这些集合的重点是允许您对其中的对象进行排序,如果对象没有自然排序顺序,那么将它们放入排序集合中的意义何在?

您应该重写 equals() 和 hashcode() 方法并改用标准 Map/Set 类。

于 2010-06-18T13:22:06.233 回答
1

如果您需要覆盖 hashCode 但不能,我认为您正在考虑扩展 HashMap 或编写自己的。

于 2010-06-18T13:24:35.580 回答
1

目前尚不清楚,但可能是您想要做的只是使用与 Set/Map 相同的语义获取某物的集合,但使用的东西没有充分实现Object.equals

在这种情况下,我建议您继承AbstractSetAbstractMap覆盖AbstractCollection.contains以使用您的 equals 版本。

我不建议这样做,但是您的问题实际上并没有说明您要达到的目标。

请参阅http://java.sun.com/javase/6/docs/api/java/util/AbstractSet.htmlhttp://java.sun.com/javase/6/docs/api/java/util/AbstractCollection .html#contains(java.lang.Object)

于 2010-06-18T14:14:33.767 回答
1

如果内存不是大问题,子类 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中缺少它

于 2010-06-18T14:29:59.020 回答
0

如果对象不可比较,则无法对它们进行排序;如果它们不可比较,你怎么知道这两个对象中的哪一个应该先出现?所以没有办法将不可比较的对象放在 a SortedMaporSortedSet中。(您为什么要这样做?使用不同类型的Mapor Set)。

Java 中的equals()方法是在 class 中定义的Object,由于所有的类都可以扩展Object,所以所有的对象都有一个equals()方法。equals()如果您希望能够判断两个对象是否相等,则必须注意在类中正确覆盖和实现。

如果您想将对象放入基于哈希的集合中(例如HashMapor HashSet),您还必须覆盖hashCode()并且您必须确保以正确的方式实现hashCode()和实现(有关如何执行此操作的详细信息,请参阅类中这些方法的文档) .equals()Object

于 2010-06-18T13:18:21.557 回答
0

正如其他人所说,如果没有自然顺序,SortedXXX 就不是真正的选择。但是,假设您只想以某种一致的方式列出元素,如果您只考虑用于相等性测试的字段,因为它们构成“主键”,并提出某种数字或字母顺序在他们周围可能适合您的目的。

于 2010-06-18T13:24:19.170 回答
0

只需在创建时使用提供给 Sorted[Set|Map] 实现的(自定义)比较器...

javadocs倾向于建议这个:All keys inserted into a sorted map must implement the Comparable interface (or be accepted by the specified comparator).

SortedSet<MyObject> s = new TreeSet<MyObject>(new Comparator<MyObject>() {
    @Override
    public int compare(T o1, T o2) {
        // your very specific, fancy dancy code here
    }
});

http://java.sun.com/javase/6/docs/api/

于 2010-06-18T13:38:30.173 回答
0

我不确定我是否明白你的意思(我认为你正试图从错误的方向解决问题),但如果你毕竟只是想要一个SetorMap来维护插入顺序,那么分别使用LinkedHashSetor LinkedHashMap


更新:根据您的报价:

我需要改变外国班级的平等行为。我无法编辑它。

在排序集/地图内?然后使用您使用自定义构造的TreeSetor 。例如TreeMapComparator

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

(它构造了一组以String不区分大小写的顺序排列的 s)。

也可以看看:

于 2010-06-18T13:38:59.010 回答
0

我不想存储排序的东西,但我会使用“通常”的 Map 和 Set,我不能“覆盖”平等行为。

您正在尝试覆盖未知类型的 equals() 方法。您正在考虑希望拥有 IEquatable 接口的问题。如果您必须使用 SortedSet/SortedMap ,请提供像@ptomli 在他的回答中提到的比较器。

改用 HashMap/HashSet 似乎是一个不错的建议。你看过那些吗?

于 2010-06-18T13:49:25.200 回答