Java:如何解决缺少Equatable接口的问题?

时间:2010-06-18 13:14:30

标签: java collections equals comparator comparable

据我所知,SortedMapSortedSet等内容在compareTo类型上使用equals(而不是Comparable<?>)来检查相等性( containscontainsKey)。

但是,如果某些类型在概念上是等同的,但可比较呢? (哈希码,内存地址,......)

我必须声明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 Peterswaxwing关于将密钥类型包装在自定义类中的想法。这样,我可以覆盖equals和hashCode,从而改变了相等行为。

13 个答案:

答案 0 :(得分: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的语义。

答案 1 :(得分: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。不适用于所有情况,但可能在您的情况下。

答案 2 :(得分:6)

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

在这种情况下,您可以使用HashMapHashSet吗?如果您不需要对其进行排序,则无需使用SortedMapSortedSet

答案 3 :(得分:1)

  

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

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

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

答案 4 :(得分:1)

  

是使用SortedMap的方法还是   SortedSet on equatable但是(by   概念)不可比的类型好   呢?

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

您应该覆盖equals()和hashcode()方法,而是使用标准的Map / Set类。

答案 5 :(得分:1)

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

答案 6 :(得分:1)

目前尚不清楚,但可能是你要做的就是使用相同的Set / Map语义来获取 somethings 的集合,但是 somethings 没有充分实施Object.equals

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

这不是我推荐的,但你的问题实际上并没有说明你想要实现的目标。

参见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)

答案 7 :(得分: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中缺失了

答案 8 :(得分:0)

如果对象无法比较,则无法对其进行排序;如果它们不具有可比性,你怎么知道这两个物体中的哪一个应该首先出现?因此,无法在SortedMapSortedSet中放置无法比较的对象。 (您为什么要这样做?使用其他类型的MapSet)。

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

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

答案 9 :(得分:0)

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

答案 10 :(得分: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/

答案 11 :(得分:0)

我不确定我是否看到了你的观点(我认为你试图从错误的方向解决问题),但如果你毕竟只想要一个SetMap维护广告订单,然后分别使用LinkedHashSetLinkedHashMap


更新:根据您的报价:

  

我需要改变外来类的相等行为。我无法编辑它。

在排序的集合/地图中?然后使用您使用自定义TreeSet构建的TreeMapComparator。 E.g。

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

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

另见:

答案 12 :(得分:0)

  

我不想存储分类的东西,   但我会使用“通常的”地图和套装,我   无法“覆盖”   平等的行为。

您正在尝试覆盖未知类型的equals()方法。您正在考虑希望您拥有IEquatable接口的问题。如果必须使用SortedSet / SortedMap,则提供类似@ptomli mentions in his answer的比较器。

使用HashMap / HashSet似乎是一个很好的建议。你看过那些吗?