如何区分Sorted集合中的两个equals对象?

时间:2012-04-25 12:41:33

标签: java guava apache-commons

我可能错了,但对我来说,我们可以覆盖对象的等号,以便你认为它们有意义地等于。 地图中的所有条目都有不同的键,集合中的所有条目都有不同的值(没有意义的等于)

但是当使用TreeMap或TreeSet时,您可以提供比较器。 我注意到,当提供比较器时,会绕过对象的equals方法,并且当比较器返回0时,两个objets被视为等于。 因此,我们有2个对象,但在地图键集或集合中,只保留一个。

我想知道是否有可能使用已排序的集合来区分两个不同的实例。

这是一个简单的样本:

public static void main(String[] args) {
    TreeSet<String> set = new TreeSet<String>();
    String s1 = new String("toto");
    String s2 = new String("toto");
    System.out.println(s1 == s2);
    set.add(s1);
    set.add(s2);
    System.out.println(set.size());
}

请注意,使用新字符串(“xxx”)会绕过String池的使用,因此s1!= s2。 我想知道如何实现比较器,使设置大小为2而不是1。

主要问题是:对于相同String值的两个不同实例,我如何在比较器中返回一些东西!= 0?

请注意,我希望比较器遵守规则:

  

比较其订单的两个参数。返回负整数,零或正整数,因为第一个参数小于,等于或大于第二个参数。实现者必须确保所有x和y的sgn(compare(x,y))== -sgn(compare(y,x))。 (这意味着当且仅当compare(y,x)抛出异常时,compare(x,y)必须抛出异常。)

     

实现者还必须确保关系是传递的:((compare(x,y)&gt; 0)&amp;&amp;(compare(y,z)&gt; 0))暗示比较(x,z)&gt ; 0

     

最后,实现者必须确保compare(x,y)== 0意味着所有z的sgn(compare(x,z))== sgn(compare(y,z))。

     

通常情况如此,但并非严格要求(compare(x,y)== 0)==(x.equals(y))。一般来说,任何违反此条件的比较器都应清楚地表明这一事实。推荐的语言是“注意:这个比较器强加了与equals不一致的排序。”

我可以使用像:

这样的技巧
public int compare(String s1,String s2) {
  if s1.equals(s2) { return -1 }
  ...
}

似乎工作正常,但是比较(s1,s2)后规则不受尊重!= -compare(s2,s1)

这个问题有没有优雅的解决方案?


编辑:对于那些想知道为什么我会问这样的事情的人。好奇心比任何现实生活中的问题更重要。

但是我已经处于这种情况,虽然关于这个问题的解决方案:

想象一下你有:

class Label {
  String label;
}

对于每个标签,您都有一个关联的String值。 现在,如果您想要一个map,label-&gt;值,该怎么办? 但是现在如果你希望能够拥有两个与地图键相同的标签呢? 防爆 “label”(ref1) - &gt;值1 “label”(ref2) - &gt;值2 您可以实现equals,以便两个不同的Label实例不等于 - &gt;我认为它适用于HashMap。

但是,如果您希望能够按字母顺序对这些Label对象进行排序,该怎么办? 您需要提供可比较的比较器或工具。 但是,我们如何才能区分具有相同标签的2个标签? 我们必须! compare(ref1,ref2)不能返回0.但是它应该返回-1还是1? 我们可以比较内存地址或类似的东西来做出这样的决定,但我认为这在Java中是不可能的......

6 个答案:

答案 0 :(得分:6)

如果您正在使用Guava,则可以使用Ordering.arbitrary(),这将对元素施加额外的顺序,这些元素在VM的生命周期内保持一致。您可以使用它以一致的方式打破比较器中的联系。

但是,您可能使用了错误的数据结构。您是否考虑过使用Multiset(例如TreeMultiset),它允许添加多个实例?

答案 1 :(得分:3)

尝试使用以下比较器(例如):

Comparator<String> comp = Ordering.natural().compound(Ordering.arbitrary());

这将根据它们的自然可比较顺序对事物进行排序,但是当自然顺序相等时,它将回退到任意顺序,以便不同的对象保持不同。

答案 2 :(得分:2)

我不确定这样做是个好主意。来自Comparator的javadoc:

  

使用能够比较的比较器时应该小心   强加与equals不一致的排序以排序有序集   (或排序的地图)。假设一个带有显式的有序集(或有序映射)   比较器c与从集合S中绘制的元素(或键)一起使用   c对S施加的排序与equals,排序后的顺序不一致   设置(或排序的地图)将表现得很奇怪。&#34;特别是排序   set(或有序映射)将违反set的一般合同(或   map),用等号定义。

答案 3 :(得分:0)

如果您希望排序的集合具有相同的对象,则可以将所有对象放在List中并使用Collections.sort()。

答案 4 :(得分:0)

您可能想要使用SortedSet<Collection<String>>或类似的,因为 - 正如您所提到的 - 排序不允许您添加多个相等的条目。

您可以使用Guava的MultiSet

来自SortedSet上的JavaDoc:

  

请注意,如果有序集合要正确实现Set接口,则由有序集合维护的排序(无论是否提供显式比较器)必须与equals 一致。

然而,仍然存在一个问题:为什么你想要两个在逻辑上相等的不同实例(equals()实际意味着什么)。

答案 5 :(得分:-2)

只有当两个引用引用相同的对象时,比较器才应该实际返回0,如下所示:

public int compare(String s1,String s2) {
   if (s1!=s2) { 
      int result = s1.compareTo(s2);
      if (result == 0) {
          return -1;
      } else {
          return result;
      }
   } else {
      return 0;
   } 
}