![](/img/trans.png)
[英]Why is the comparator used instead of the equals() in Collections?
[英]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 以来添加到CollectionUtils
新isEqualCollection
方法。该方法使用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.