使用 Comparator 而不是 equals() 比较两个 Java 集合
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15094818/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Compare two Java Collections using Comparator instead of equals()
提问by Matt Lachman
Problem Statement
问题陈述
I have two Collections of the same type of object that I want to compare. In this case, I want to compare them based on an attribute that does not factor into equals()
for the Objects. In my example, I'm using ranked collections of Names for instance:
我有两个要比较的相同类型对象的集合。在这种情况下,我想根据不考虑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 want to compare the two Collections to assert that, for position i
in each Collection, the weightedRank
of each Name at that position is the same value. I did some Googling but didn't find a suitable method in Commons Collections or any other API so I came up with the following:
我想比较两个集合以断言,对于i
每个集合weightedRank
中的位置,该位置的每个 Name 的值是相同的。我做了一些谷歌搜索,但没有在 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;
}
Question
问题
Is there another way to do this? Did I miss an obvious method from Commons Collections?
有没有其他方法可以做到这一点?我是否错过了 Commons Collections 中的一个明显方法?
Related
有关的
I also spotted this questionon SO which is similar though in that case I'm thinking overriding equals()
makes a little more sense.
我也在SO 上发现了这个问题,虽然在这种情况下我认为覆盖equals()
更有意义。
Edit
编辑
Something very similar to this will be going into a release of Apache Commons Collectionsin the near future (at the time of this writing). See https://issues.apache.org/jira/browse/COLLECTIONS-446.
在不久的将来(在撰写本文时),Apache Commons Collections的发布中将包含与此非常相似的内容。请参阅https://issues.apache.org/jira/browse/COLLECTIONS-446。
采纳答案by sharakan
I'm not sure this way is actually better, but it is "another way"...
我不确定这种方式实际上更好,但它是“另一种方式”......
Take your original two collections, and create new ones containing an Adapter for each base object. The Adapter should have .equals()
and .hashCode()
implemented as being based on Name.calculateWeightedRank()
. Then you can use normal Collection equality to compare the collections of Adapters.
使用您原来的两个集合,并为每个基础对象创建包含一个 Adapter 的新集合。适配器应该具有.equals()
并.hashCode()
实现为基于Name.calculateWeightedRank()
. 然后你可以使用普通的 Collection 相等来比较 Adapters 的集合。
* Edit *
* 编辑 *
Using Eclipse's standard hashCode/equals generation for the Adapter
. Your code would just call adaptCollection on each of your base collections, then List.equals() the two results.
使用 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;
}
}
回答by lbalazscs
You could use the Guava Equivalenceclass in order to decouple the notions of "comparing" and "equivalence". You would still have to write your comparing method (AFAIK Guava does not have it) that accepts an Equivalence subclass instead of the Comparator, but at least your code would be less confusing, and you could compare your collections based on any equivalence criteria.
您可以使用 Guava Equivalence类来分离“比较”和“等价”的概念。您仍然需要编写接受 Equivalence 子类而不是 Comparator 的比较方法(AFAIK Guava 没有),但至少您的代码不会那么混乱,并且您可以根据任何等价标准比较您的集合。
Using a collection of equivance-wrapped objects (see the wrap method in Equivalence) would be similar to the Adapter-based solution proposed by sharakan, but the equivalence implementation would be decoupled from the adapter implementation, allowing you to easily use multiple Equivalence criteria.
使用一组 equivance-wrapped 对象(参见Equivalence 中的wrap 方法)类似于 sharakan 提出的基于适配器的解决方案,但等价实现将与适配器实现分离,允许您轻松使用多个等价标准。
回答by nndru
You can use new isEqualCollection
method added to CollectionUtils
since version 4. This method uses external comparsion mechanism provided by Equator
interface implementation. Please, check this javadocs: CollectionUtils.isEqualCollection(...)and Equator.
您可以使用从版本 4 开始isEqualCollection
添加的新方法。CollectionUtils
该方法使用Equator
接口实现提供的外部比较机制。请检查这个 javadocs:CollectionUtils.isEqualCollection(...)和Equator。
回答by nattyddubbs
EDIT: Removed old answer.
编辑:删除旧答案。
Another option that you have is creating an interface called Weighted
that could look like this:
您拥有的另一个选择是创建一个名为的接口Weighted
,它可能如下所示:
public interface Weighted {
int getWeightedRank();
}
Then have your Name
class implement this interface. Then you could change your method to look like this:
然后让你的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;
}
Then as you find additional classes that need to be weighted and compared you can put them in your collection and they could be compared with each other as well. Just an idea.
然后,当您发现需要加权和比较的其他类时,您可以将它们放入您的集合中,它们也可以相互比较。只是一个想法。