18

问题陈述

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

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每个集合中的位置,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

4

4 回答 4

6

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

使用等价包装对象的集合(请参阅Equivalence 中的 wrap 方法)将类似于 sharakan 提出的基于适配器的解决方案,但等价实现将与适配器实现分离,允许您轻松使用多个等价标准。

于 2013-02-26T18:45:28.413 回答
5

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

于 2015-07-27T09:21:48.947 回答
1

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

获取原始的两个集合,并为每个基础对象创建包含一个适配器的新集合。适配器应该具有.equals().hashCode()实现为基于Name.calculateWeightedRank(). 然后你可以使用普通的集合相等来比较适配器的集合。

* 编辑 *

使用 Eclipse 的标准 hashCode/equals 生成Adapter. 您的代码只需在每个基本集合上调用 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;
    }

}
于 2013-02-26T17:04:05.243 回答
0

编辑:删除旧答案。

您拥有的另一个选项是创建一个名为的接口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;
}

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

于 2013-02-26T17:11:38.387 回答