繁体   English   中英

使用 Comparator 而不是 equals() 比较两个 Java 集合

[英]Compare two Java Collections using Comparator instead of equals()

问题陈述

我有两个要比较的相同类型对象的集合。 在这种情况下,我想根据不考虑对象的equals()的属性来比较它们。 在我的示例中,我正在使用名称的排名集合,例如:

public class Name {
    private String name;
    private int weightedRank;

    //getters & setters

    @Override
    public boolean equals(Object obj) {
        return this.name.equals(obj.name); //Naive implementation just to show
                                           //equals is based on the name field.
    }
}

我想比较两个集合以断言,对于每个集合中的位置i ,该位置的每个 Name 的weightedRank是相同的值。 我做了一些谷歌搜索,但没有在 Commons Collections 或任何其他 API 中找到合适的方法,所以我想出了以下内容:

public <T> boolean comparatorEquals(Collection<T> col1, Collection<T> col2,
        Comparator<T> c)
{
    if (col1 == null)
        return col2 == null;
    if (col2 == null) 
        return false;

    if (col1.size() != col2.size())
        return false;

    Iterator<T> i1 = col1.iterator(), i2 = col2.iterator();

    while(i1.hasNext() && i2.hasNext()) {
        if (c.compare(i1.next(), i2.next()) != 0) {
            return false;
        }
    }

    return true;
}

问题

有没有另一种方法可以做到这一点? 我是否错过了 Commons Collections 中的一个明显方法?

有关的

我也在 SO 上发现了这个问题,尽管在这种情况下我认为覆盖equals()更有意义。

编辑

在不久的将来(在撰写本文时), Apache Commons Collections的发布中将包含与此非常相似的内容。 请参阅https://issues.apache.org/jira/browse/COLLECTIONS-446

您可以使用 Guava Equivalence类来分离“比较”和“等价”的概念。 您仍然需要编写接受 Equivalence 子类而不是 Comparator 的比较方法(AFAIK Guava 没有),但至少您的代码不会那么混乱,并且您可以根据任何等价标准比较您的集合。

使用一组 equivance-wrapped 对象(参见Equivalence 中wrap 方法)类似于 sharakan 提出的基于适配器的解决方案,但等价实现将与适配器实现分离,允许您轻松使用多个等价标准。

您可以使用自版本 4 以来添加到CollectionUtilsisEqualCollection方法。该方法使用Equator接口实现提供的外部比较机制。 请检查这个javadocs: CollectionUtils.isEqualCollection(...)Equator

我不确定这种方式实际上更好,但它是“另一种方式”......

使用您原来的两个集合,并为每个基础对象创建包含一个 Adapter 的新集合。 Adapter 应该将.equals().hashCode()实现为基于Name.calculateWeightedRank() 然后你可以使用普通的 Collection 相等来比较 Adapters 的集合。

* 编辑 *

Adapter使用 Eclipse 的标准 hashCode/equals 生成。 您的代码只会在您的每个基本集合上调用 adaptCollection,然后 List.equals() 两个结果。

public class Adapter {

    public List<Adapter> adaptCollection(List<Name> names) {
        List<Adapter> adapters = new ArrayList<Adapter>(names.size());

        for (Name name : names) {
            adapters.add(new Adapter(name));
        }

        return adapters;
    }


    private final int name;

    public Adapter(Name name) {
        this.name = name.getWeightedResult();
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + name;
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Adapter other = (Adapter) obj;
        if (name != other.name)
            return false;
        return true;
    }

}

编辑:删除旧答案。

您拥有的另一个选择是创建一个名为Weighted的界面,它可能如下所示:

public interface Weighted {
    int getWeightedRank();
}

然后让你的Name类实现这个接口。 然后你可以改变你的方法看起来像这样:

 public <T extends Weighted> boolean weightedEquals(Collection<T> col1, Collection<T> col2)
{
    if (col1 == null)
      return col2 == null;
     if (col2 == null) 
      return false;

  if (col1.size() != col2.size())
      return false;

  Iterator<T> i1 = col1.iterator(), i2 = col2.iterator();

  while(i1.hasNext() && i2.hasNext()) {
      if (i1.next().getWeightedRank() != i2.next().getWeightedRank()) {
          return false;
      }
  }

  return true;
}

然后,当您发现需要加权和比较的其他类时,您可以将它们放入您的集合中,它们也可以相互比较。 只是一个想法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM