鉴于任何两个类,例如下面有ClassA
和ClassB
:
class ClassA {
private int intA;
private String strA;
private boolean boolA;
// Constructor
public ClassA (int intA, String strA, boolean boolA) {
this.intA = intA; this.strA = strA; this.boolA = boolA;
} // Getters and setters etc. below...
}
class ClassB {
private int intB;
private String strB;
private boolean boolB;
// Constructor
public ClassB (int intB, String strB, boolean boolB) {
this.intB = intB; this.strB = strB; this.boolB = boolB;
} // Getters and setters etc. below...
}
以及任何两种不同的Collection
类型,一种具有ClassA
元素,另一种具有ClassB
元素,例如:
List<Object> myList = Arrays.asList(new ClassA(1, "A", true),
new ClassA(2, "B", true));
Set<Object> mySet = new HashSet<Object>(
Arrays.asList(new ClassB(1, "A", false),
new ClassB(2, "B", false)));
根据指定的字段子集,判断两个Collection
是否等同于&#34;(*)的最简单方法是什么?
(*)单词&#34;等同于&#34;使用而不是&#34;相等&#34;因为这是语境 - 即这样的&#34;等价&#34;可能在另一个背景下有不同的定义。
上面的示例:
假设我们指定intA
和strA
分别与intB
和strB
匹配(但boolA
/ boolB
值可以忽略)。这将使上面定义的两个集合对象被认为是等价的 - 但是如果一个元素被添加到其中一个集合或从其中删除,那么它们将不再是。
首选解决方案: 对于任何Collection
类型,使用的方法都应该是通用的。理想情况下Java 7仅限于使用它(但Java 8可能对其他人有额外的兴趣)。很高兴使用Guava或Apache Commons,但不想使用更加模糊的外部库。
答案 0 :(得分:8)
这是使用lambdas和高阶函数的Java 8版本。可能使用匿名内部类而不是lambdas将其转换为Java 7。 (我相信大多数IDE都有重构操作来执行此操作。)我将此作为练习留给感兴趣的读者。
这里实际上有两个不同的问题:
给定两个不同类型的对象,通过检查每个对象的相应字段来评估它们。这与&#34; equals&#34;不同。并且&#34;比较&#34;已经由JDK库API定义的操作,因此我将使用术语&#34;等效的&#34;代替。
鉴于两个集合包含这些类型的元素,请确定它们是否为&#34;等于&#34;对于该术语的某些定义。这实际上非常微妙;见下面的讨论。
<强> 1。等价强>
鉴于两个类型T
和U
的对象,我们想确定它们是否相同。结果是布尔值。这可以由类型BiPredicate<T,U>
的函数表示。但我们不能直接检查对象;相反,我们需要从每个对象中提取相应的字段,并相互评估提取结果。如果从T
提取的字段属于TR
类型,而从U
提取的字段属于UR
类型,则提取器由函数类型表示
Function<T, TR>
Function<U, UR>
现在我们提取了TR
和UR
类型的结果。我们可以在它们上面调用equals()
,但这会造成不必要的限制。相反,我们可以提供另一个等价函数,它将被调用以相互评估这两个结果。那是BiPredicate<TR,UR>
。
鉴于这一切,我们可以编写一个更高阶的函数,它接受所有这些函数并为我们生成和等价函数(包含完整性的通配符):
static <T,U,TR,UR> BiPredicate<T,U> equiv(Function<? super T, TR> tf,
Function<? super U, UR> uf,
BiPredicate<? super TR, ? super UR> pred) {
return (t, u) -> pred.test(tf.apply(t), uf.apply(u));
}
使用equals()
评估字段提取结果可能是一种常见情况,因此我们可以为此提供重载:
static <T,U> BiPredicate<T,U> equiv(Function<? super T, ?> tf,
Function<? super U, ?> uf) {
return (t, u) -> equiv(tf, uf, Object::equals).test(t, u);
}
我本可以提供另一个类型变量R
作为两个函数的结果类型,以确保它们属于同一类型,但事实证明这并非必要。由于在equals()
上定义了Object
并且它需要Object
个参数,因此我们实际上并不关心函数返回类型是什么,因此也就是通配符。
以下是如何使用它来仅使用字符串字段来评估OP的示例类:
ClassA a = ... ;
ClassB b = ... ;
if (equiv(ClassA::getStrA, ClassB::getStrB).test(a, b)) {
// they're equivalent
}
作为一种变体,我们可能还需要原始的专业化以避免不必要的装箱:
static <T,U> BiPredicate<T,U> equivInt(ToIntFunction<? super T> tf,
ToIntFunction<? super U> uf) {
return (t, u) -> tf.applyAsInt(t) == uf.applyAsInt(u);
}
这使我们可以基于单个字段构造等价函数。如果我们想基于多个字段评估等价,该怎么办?我们可以通过链接and()
方法组合任意数量的BiPredicates。以下是如何使用OP示例中的类的int
和String
字段创建评估等效性的函数。为此,最好将函数存储在一个变量中,而不是使用它,尽管这可能都是内联的(我认为这会让它变得不可读):
BiPredicate<ClassA, ClassB> abEquiv =
equivInt(ClassA::getIntA, ClassB::getIntB)
.and(equiv(ClassA::getStrA, ClassB::getStrB));
if (abEquiv.test(a, b)) {
// they're equivalent
}
作为最后一个例子,在为两个类创建等价函数时,能够为字段提取结果提供等价函数是非常强大的。例如,假设我们想要提取两个String字段,并且如果提取的字符串是等于的,则认为它们是等价的,忽略大小写。以下代码会生成true
:
equiv(ClassA::getStrA, ClassB::getStrB, String::equalsIgnoreCase)
.test(new ClassA(2, "foo", true),
new ClassB(3, "FOO", false))
<强> 2。收集“平等”
第二部分是评估两个集合是否等于&#34;等于&#34;在某种意义上。问题是在集合框架中,定义了相等的概念,使得List只能等于另一个List,而Set只能等于另一个Set。因此,其他类型的Collection永远不能等于List或Set。有关这一点的一些讨论,请参阅Collection.equals()
的规范。
这显然与OP想要的不一样。正如OP所建议的那样,我们并不真正想要&#34;平等,&#34;但是我们想要一些我们需要提供定义的其他属性。根据OP的例子以及Przemek Gumula和janos在其他答案中的一些建议,似乎我们希望两个集合中的元素以某种方式一一对应。我将此称为bijection,这可能不是数学上的精确,但它似乎足够接近。此外,每对元素之间的对应关系应该是等价,如上所述。
计算这个有点微妙,因为我们有自己的等价关系。我们无法使用许多内置集合操作,因为它们都使用equals()
。我的第一次尝试是这样的:
// INCORRECT
static <T,U> boolean isBijection(Collection<T> c1,
Collection<U> c2,
BiPredicate<? super T, ? super U> pred) {
return c1.size() == c2.size() &&
c1.stream().allMatch(t -> c2.stream()
.anyMatch(u -> pred.test(t, u)));
}
(这与Przemek Gumula给出的基本相同。)这有问题,可归结为一个集合中多个元素对应于另一个集合中的单个元素的可能性,留下元素无法比拟的。如果给定两个多重集,使用相等作为等价函数,则会产生奇怪的结果:
{a x 2, b} // essentially {a, a, b}
{a, b x 2} // essentially {a, b, b}
这个函数认为这两个多重集合是一个双射,但事实并非如此。如果等价函数允许多对一匹配,则会出现另一个问题:
Set<String> set1 = new HashSet<>(Arrays.asList("foo", "FOO", "bar"));
Set<String> set2 = new HashSet<>(Arrays.asList("fOo", "bar", "quux"));
isBijection(set1, set2, equiv(s -> s, s -> s, String::equalsIgnoreCase))
结果为true
,但如果以相反的顺序给出集合,则结果为false
。这显然是错的。
另一种算法是创建临时结构并删除匹配的元素。结构必须考虑重复,因此我们需要递减计数,并且只在计数达到零时才删除元素。幸运的是,各种Java 8功能使这非常简单。这与janos的答案中使用的算法非常相似,尽管我已经将等价函数提取到方法参数中。唉,因为我的等价函数可以有嵌套的等价函数,这意味着我无法探测映射(由相等定义)。相反,我必须搜索地图的键,这意味着算法是O(N ^ 2)。哦,好吧。
然而,代码非常简单。首先,使用groupingBy
从第二个集合生成频率图。然后,迭代第一个集合的元素,并搜索频率映射的键以获得等价物。如果找到一个,则其出现次数递减。请注意传递给Map.compute()
的重映射函数的null
的返回值。这具有删除条目的副作用,而不是将映射设置为null
。它有点像API黑客,但它非常有效。
对于第一个集合中的每个元素,必须找到第二个集合中的等效元素,否则它将失效。在处理完第一个集合的所有元素之后,频率图中的所有元素也应该被处理,因此它只是被测试为空。
以下是代码:
static <T,U> boolean isBijection(Collection<T> c1,
Collection<U> c2,
BiPredicate<? super T, ? super U> pred) {
Map<U, Long> freq = c2.stream()
.collect(Collectors.groupingBy(u -> u, Collectors.counting()));
for (T t : c1) {
Optional<U> ou = freq.keySet()
.stream()
.filter(u -> pred.test(t, u))
.findAny();
if (ou.isPresent()) {
freq.compute(ou.get(), (u, c) -> c == 1L ? null : c - 1L);
} else {
return false;
}
}
return freq.isEmpty();
}
这个定义是否正确并不完全清楚。但直觉上似乎是人们想要的东西。但它很脆弱。如果等价函数不对称,isBijection
将失败。也有一些自由度并没有被考虑在内。例如,假设集合是
{a, b}
{x, y}
a
相当于x
和y
,但b
仅相当于x
。如果a
与x
匹配,则isBijection
的结果为false
。但如果a
与y
匹配,则结果为true
。
将它放在一起
这是OP的示例,使用equiv()
,equivInt()
和isBijection
函数进行编码:
List<ClassA> myList = Arrays.asList(new ClassA(1, "A", true),
new ClassA(2, "B", true));
Set<ClassB> mySet = new HashSet<>(Arrays.asList(new ClassB(1, "A", false),
new ClassB(2, "B", false)));
BiPredicate<ClassA, ClassB> abEquiv =
equivInt(ClassA::getIntA, ClassB::getIntB)
.and(equiv(ClassA::getStrA, ClassB::getStrB));
isBijection(myList, mySet, abEquiv)
结果是true
。
答案 1 :(得分:7)
另一种可能的解决方案是使用谓词编写一个简单的比较方法(因此您可以明确指定两个类的条件在您的术语上相似)。我在Java 8中创建了这个:
<T, U> boolean compareCollections(Collection<T> coll1, Collection<U> coll2, BiPredicate<T, U> predicate) {
return coll1.size() == coll2.size()
&& coll1.stream().allMatch(
coll1Item -> coll2.stream().anyMatch(col2Item -> predicate.test(coll1Item, col2Item))
);
}
如您所见,它会比较大小,然后检查集合中的每个元素是否在第二个集合中都有对应物(虽然它不是比较顺序)。它是在Java 8中,但您可以通过实现一个简单的BiPredicate代码将其移植到Java 7,allMatch和anyMatch(每个代码都应该足够用于循环)
编辑:Java 7代码:
<T, U> boolean compareCollections(Collection<T> coll1, Collection<U> coll2, BiPredicate<T, U> predicate) {
if (coll1.size() != coll2.size()) {
return false;
}
for (T item1 : coll1) {
boolean matched = false;
for (U item2 : coll2) {
if (predicate.test(item1, item2)) {
matched = true;
}
}
if (!matched) {
return false;
}
}
return true;
}}
interface BiPredicate <T, U> {
boolean test(T t, U u);
}
答案 2 :(得分:3)
没有非常简单的方法。
与常规Java集合一起使用的最通用的是创建一个包装类,它将ClassA
或ClassB
作为输入,然后覆盖由您定义的equals / hashcode。 / p>
在某些情况下,您可以滥用Comparator
,但这会将您限制为TreeMap/TreeSet
。
您还可以实现equals()
方法,以便classA.equals(classB);
返回true,但如果您不小心,这可能会导致棘手的错误。它还会导致a.equals(b)
和b.equals(c)
但!a.equals(c)
的有趣情况。
某些库(Guava?)还有一个Comparator
样式的机制进行相等性测试,但这只适用于库的集合。
答案 3 :(得分:2)
Apache Commons Lang有EqualsBuilder#reflectionEquals(Object, Object)
:
此方法使用反射来确定两个
Object
是否相等。它使用
AccessibleObject.setAccessible
来访问私有 领域。这意味着如果运行它将抛出安全异常 在安全管理器下,如果未正确设置权限。 它也没有明确测试那么高效。非原始 使用equals()
比较字段。瞬态成员将不会被测试,因为它们可能是派生的 字段,而不是
Object
的值的一部分。不会测试静态字段。将包括超类字段。
所以这应该涵盖你的用例。明显的免责声明:它使用反射;)
编辑:当然,这假设字段具有相同的名称,而不是类型。在后一种情况下,可以检查source code并将其调整为用例。
答案 4 :(得分:2)
两个现有答案的组合:Kayaman建议的封装类的通用版本(Just a List)。使用ArrayList :: equals作为Przemek Gumula方法的谓词。
我添加了一个Builder,使其使用起来更好:
StructureEqual<ClassA, ClassB> struct = StructureEqual.<ClassA, ClassB>builder()
.field(ClassA::getIntA, ClassB::getIntB) // Declare what fields should be checked
.field(ClassA::getStrA, ClassB::getStrB)
.build();
System.out.println(struct.isEqual(myList, mySet));
实际代码:
public class StructureEqual<A, B> {
private List<EqualPoint<A, B>> points;
public StructureEqual(List<EqualPoint<A, B>> points) {
this.points = points;
}
private List<Object> sampleA(A a) {
return points.stream().map(p -> p.getAPoint().apply(a)).collect(Collectors.toList());
}
private List<Object> sampleB(B b) {
return points.stream().map(p -> p.getBPoint().apply(b)).collect(Collectors.toList());
}
public boolean isEqual(Collection<A> as, Collection<B> bs) {
Set<List<Object>> aSamples = as.stream().map(this::sampleA).collect(Collectors.toSet());
Set<List<Object>> bSamples = bs.stream().map(this::sampleB).collect(Collectors.toSet());
return aSamples.equals(bSamples);
}
private static class EqualPoint<PA, PB> {
private final Function<PA, ?> aPoint;
private final Function<PB, ?> bPoint;
public <T> EqualPoint(Function<PA, T> aPoint, Function<PB, T> bPoint) {
this.aPoint = aPoint;
this.bPoint = bPoint;
}
Function<PA, ?> getAPoint() {
return aPoint;
}
Function<PB, ?> getBPoint() {
return bPoint;
}
}
public static <BA, BB> Builder<BA, BB> builder() {
return new Builder<>();
}
public static class Builder<BA, BB> {
private List<EqualPoint<BA, BB>> points = new ArrayList<>();
public <T> Builder<BA, BB> field(Function<BA, T> a, Function<BB, T> b) {
points.add(new EqualPoint<>(a, b));
return this;
}
public StructureEqual<BA, BB> build() {
return new StructureEqual<>(Collections.unmodifiableList(points));
}
}
}
答案 5 :(得分:2)
根据指定的字段子集判断两个
Collections
是否相等的最简单方法是什么?
根据您的描述,您的平等要求是:
item1
中的每个collection1
,item2
中存在collection2
,item1.field_x
等于item2.field_y
,对于多个定义{ {1}} - field_x
对。如果我们可以假设两个集合中都没有重复的元素, 也就是说,这是最简单的方式&#34;可能是这样的:
field_y
这是对要求的直接实施。
但是因为它可以将每个元素与其他集合中的每个其他元素进行比较,
它性能很差,
public boolean areEqual(Collection<ClassA> c1, Collection<ClassB> c2) {
if (c1.size() != c2.size()) {
return false;
}
OUTER:
for (ClassA a : c1) {
for (ClassB b : c2) {
if (a.getIntA() == b.getIntB() && Objects.equals(a.getStringA(), b.getStringB())) {
continue OUTER;
}
}
return false;
}
return true;
}
其中O(n^2)
是集合的大小。
如果相同的元素可以在集合中多次出现,这也可能无效:
n
此处 List<ClassA> list = new ArrayList<>(Arrays.asList(
new ClassA(1, "A", true),
new ClassA(1, "A", false),
new ClassA(2, "B", true)
));
Set<ClassB> set = new HashSet<>(Arrays.asList(
new ClassB(1, "A", false),
new ClassB(2, "B", false),
new ClassB(2, "B", true)
));
和ClassA(1, "A", true)
在第一个列表中被视为等效,ClassA(1, "A", false)
和new ClassB(2, "B", false)
在第二个列表中被视为等效。
上面的算法会发现这两个集合相等,这是不正确的。
可以处理重复的情况,同时以牺牲额外空间为代价来提高时间复杂度:
new ClassB(2, "B", true)
元组(int, String)
元组,(int, String)
,因为这意味着元素没有匹配的对return false
。实现:
return true
一些断言测试来验证实现:
class FieldExtractingEqual {
public boolean areEqual(Collection<ClassA> c1, Collection<ClassB> c2) {
if (c1.size() != c2.size()) {
return false;
}
Map<Tuple, Integer> counts = new HashMap<>();
for (ClassA a : c1) {
Tuple tuple = new Tuple(a.getIntA(), a.getStringA());
Integer count = counts.get(tuple);
if (count == null) {
count = 0;
}
counts.put(tuple, count + 1);
}
for (ClassB b : c2) {
Tuple tuple = new Tuple(b.getIntB(), b.getStringB());
Integer count = counts.get(tuple);
if (count == null) {
return false;
}
if (count == 1) {
counts.remove(tuple);
} else {
counts.put(tuple, count - 1);
}
}
return true;
}
private static class Tuple {
private final Object[] values;
public Tuple(Object... values) {
this.values = values;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Tuple tuple = (Tuple) o;
return Arrays.equals(values, tuple.values);
}
@Override
public int hashCode() {
return Arrays.hashCode(values);
}
}
}
作为进一步改进,
可以使List<ClassA> myList = new ArrayList<>(Arrays.asList(
new ClassA(1, "A", true),
new ClassA(1, "A", true),
new ClassA(2, "B", true)
));
Set<ClassB> mySet = new HashSet<>(Arrays.asList(
new ClassB(1, "A", false),
new ClassB(1, "A", true),
new ClassB(2, "B", false)
));
FieldExtractingEqual comp = new FieldExtractingEqual();
assertThat(comp.areEqual(myList, mySet)).isTrue();
myList.add(new ClassA(3, "X", true));
mySet.add(new ClassB(3, "Y", true));
assertThat(comp.areEqual(myList, mySet)).isFalse();
泛型的实现,
这样它可以采用任意FieldExtractingEqual
和Collection<A>
个参数,
和提取器对用于创建来自Collection<B>
和A
的元组。
这是实现这一目标的一种方法:
B
示例用法和一些断言测试:
interface FieldExtractor<T, V> {
V apply(T arg);
}
class GenericFieldExtractingEqual<T, U> {
private final List<FieldExtractor<T, ?>> extractors1;
private final List<FieldExtractor<U, ?>> extractors2;
private GenericFieldExtractingEqual(List<FieldExtractor<T, ?>> extractors1, List<FieldExtractor<U, ?>> extractors2) {
this.extractors1 = extractors1;
this.extractors2 = extractors2;
}
public boolean areEqual(Collection<T> c1, Collection<U> c2) {
if (c1.size() != c2.size()) {
return false;
}
Map<Tuple, Integer> counts = new HashMap<>();
for (T a : c1) {
Tuple tuple = newTuple1(a);
Integer count = counts.get(tuple);
if (count == null) {
count = 0;
}
counts.put(tuple, count + 1);
}
for (U b : c2) {
Tuple tuple = newTuple2(b);
Integer count = counts.get(tuple);
if (count == null) {
return false;
}
if (count == 1) {
counts.remove(tuple);
} else {
counts.put(tuple, count - 1);
}
}
return true;
}
private Tuple newTuple1(T a) {
return new Tuple(extractors1.stream().map(x -> x.apply(a)).toArray());
}
private Tuple newTuple2(U b) {
return new Tuple(extractors2.stream().map(x -> x.apply(b)).toArray());
}
private static class Tuple {
private final Object[] values;
public Tuple(Object... values) {
this.values = values;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Tuple tuple = (Tuple) o;
return Arrays.equals(values, tuple.values);
}
@Override
public int hashCode() {
return Arrays.hashCode(values);
}
}
public static class Builder<T, U> {
List<FieldExtractor<T, ?>> extractors1 = new ArrayList<>();
List<FieldExtractor<U, ?>> extractors2 = new ArrayList<>();
<V> Builder<T, U> addExtractors(FieldExtractor<T, V> extractor1, FieldExtractor<U, V> extractor2) {
extractors1.add(extractor1);
extractors2.add(extractor2);
return this;
}
GenericFieldExtractingEqual<T, U> build() {
return new GenericFieldExtractingEqual<>(new ArrayList<>(extractors1), new ArrayList<>(extractors2));
}
}
}
也就是说,您从提取器对中构建GenericFieldExtractingEqual<ClassA, ClassB> comp2 = new GenericFieldExtractingEqual.Builder<ClassA, ClassB>()
.addExtractors(ClassA::getIntA, ClassB::getIntB)
.addExtractors(ClassA::getStringA, ClassB::getStringB)
.build();
assertThat(comp2.areEqual(myList, mySet)).isTrue();
myList.add(new ClassA(3, "X", true));
mySet.add(new ClassB(3, "Y", true));
assertThat(comp2.areEqual(myList, mySet)).isFalse();
实例,例如:
GenericFieldExtractingEqual
第一个参数是一个提取第一个类中的字段的对象, 第二个参数是一个提取第二个类中相应字段的对象。 您可以添加任意数量的提取器对,以便比较相等条件。
虽然我使用Java8写作风格 .addExtractors(ClassA::getIntA, ClassB::getIntB)
来实现紧凑性,
转换为ClassA::getIntA
实现很容易(但很冗长):
FieldExtractor
.addExtractors(
new FieldExtractor<ClassA, Integer>() {
@Override
public Integer apply(ClassA arg) {
return arg.getIntA();
}
},
new FieldExtractor<ClassB, Integer>() {
@Override
public Integer apply(ClassB arg) {
return arg.getIntB();
}
}
)
实用程序方法也是如此。
答案 6 :(得分:1)
以下是我的回答:
public class StackOverFlow {
static class Testy {
int id;
String name;
public Testy(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public int hashCode() {
int hash = 3;
hash = 89 * hash + this.id;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Testy other = (Testy) obj;
if (this.id != other.id || !this.name.equals(other.name)) {
return false;
}
return true;
}
}
static class AnotherTesty {
int id;
String name;
public AnotherTesty(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public int hashCode() {
int hash = 5;
hash = 41 * hash + this.id;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final AnotherTesty other = (AnotherTesty) obj;
if (this.id != other.id || !this.name.equals(other.name)) {
return false;
}
return true;
}
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
List<Object> list = Arrays.asList(new Testy(5, "test"), new AnotherTesty(5, "test"));
Set<Object> set = new HashSet<>(Arrays.asList(new Testy(5, "test"), new AnotherTesty(5, "test")));
System.out.println(compareCollections(list, set, Testy.class, AnotherTesty.class));
}
private static boolean compareCollections(Collection<?> c1, Collection<?> c2, Class cls, Class cls2) {
List<Object> listOfCls = c1.stream().filter(p -> cls.isInstance(p)).map(o -> cls.cast(o)).collect(Collectors.toList());
List<Object> listOfCls2 = c1.stream().filter(p -> cls2.isInstance(p)).map(o -> cls2.cast(o)).collect(Collectors.toList());
List<Object> list2OfCls = c2.stream().filter(p -> cls.isInstance(p)).map(o -> cls.cast(o)).collect(Collectors.toList());
List<Object> list2OfCls2 = c2.stream().filter(p -> cls2.isInstance(p)).map(o -> cls2.cast(o)).collect(Collectors.toList());
if (listOfCls.size() != list2OfCls.size()||listOfCls2.size() != list2OfCls2.size()) {
return false;
}
boolean clsFlag = true, cls2Flag = true;
for (int i = 0; i < listOfCls.size(); i++) {
if (!listOfCls.get(i).equals(list2OfCls.get(i))) {
clsFlag = false;
break;
}
}
for (int i = 0; i < list2OfCls2.size(); i++) {
if (!listOfCls2.get(i).equals(list2OfCls2.get(i))) {
cls2Flag = false;
break;
}
}
return clsFlag && cls2Flag;
}
}
答案 7 :(得分:1)
快速原型:
package stackoverflow;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
import org.junit.Test;
public class CompareTwoList {
static class ClassA {
int intA;
String strA;
boolean boolA;
// Constructor
public ClassA(int intA, String strA, boolean boolA) {
this.intA = intA;
this.strA = strA;
this.boolA = boolA;
} // Getters and setters etc. below...
}
static class ClassB {
int intB;
String strB;
boolean boolB;
// Constructor
public ClassB(int intB, String strB, boolean boolB) {
this.intB = intB;
this.strB = strB;
this.boolB = boolB;
} // Getters and setters etc. below...
}
@FunctionalInterface
private interface IncopatibeEqualsOperator<A, B> extends BiFunction<A, B, Boolean> {
}
@Test
public void CompareListOfClassAAndclassBObjects() throws Exception {
List<ClassA> myList = Arrays.asList(
new ClassA(1, "A", true),
new ClassA(2, "B", true));
Set<ClassB> mySet = new HashSet<ClassB>(Arrays.asList(
new ClassB(1, "A", false),
new ClassB(2, "B", false)));
// can be extract to separate file
IncopatibeEqualsOperator<ClassA, ClassB> equalsOperatorFlavor1 = (ClassA o1, ClassB o2) -> {
// custom logic here
return o1.intA == o2.intB &&
java.util.Objects.equals(o1.strA, o2.strB);
};
boolean areEquals = areEquals(myList, mySet, equalsOperatorFlavor1);
assertThat(areEquals, is(true));
}
// Add in utility class
private <A, B> boolean areEquals(Collection<A> o1, Collection<B> o2, IncopatibeEqualsOperator<A, B> comparator) {
if (o1.size() == o2.size()) { // if size different; they are not equals
for (A obj1 : o1) {
boolean found = false; // search item of o1 into o2; algorithm
// can be improve
for (B obj2 : o2) {
if (comparator.apply(obj1, obj2)) { // call custom code of
// comparision
found = true;
break;
}
}
if (!found) {// if current element of o1 is not equals with any
// one return false
return false;
}
}
return true;// all are matched
}
return false;
}
}
答案 8 :(得分:1)
确保A类和B类具有toString()方法。
<强> ClassA的强>
public class ClassA {
private int intA;
private String strA;
private boolean boolA;
// Constructor
public ClassA (int intA, String strA, boolean boolA) {
this.intA = intA; this.strA = strA; this.boolA = boolA;
} //
@Override
public String toString()
{
return intA + " " + strA + " " + boolA;
}
}
<强> ClassB的强>
public class ClassB {
private int intB;
private String strB;
private boolean boolB;
// Constructor
public ClassB (int intB, String strB, boolean boolB) {
this.intB = intB; this.strB = strB; this.boolB = boolB;
} // Gett
@Override
public String toString()
{
return intB + " " + strB + " " + boolB;
}
}
主要/测试强>
public class JavaApplication11 {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
List<Object> myList = Arrays.asList(new ClassA(1, "A", true),
new ClassA(2, "B", true));
Set<Object> mySet = new HashSet<Object>(
Arrays.asList(new ClassB(1, "A", false),
new ClassB(2, "B", false)));
System.out.println("is equal: " + isEqual(myList, mySet));
}
static boolean isEqual(Object list, Object set)
{
System.out.println(list.toString());
System.out.println(set.toString());
String tempStringA = list.toString();
tempStringA = tempStringA.replaceAll("true", "");
tempStringA = tempStringA.replaceAll("false", "");
String tempStringB = set.toString();
tempStringB = tempStringB.replaceAll("true", "");
tempStringB = tempStringB.replaceAll("false", "");
return tempStringA.equals(tempStringB);
}
}
答案 9 :(得分:1)
你应该采用EqualsBuilder的基本思想,但根据你的需要进行修改:创建某种类型的列表,与成员(或更好的吸气剂)进行比较,例如。一个HashMap。现在迭代此映射,使用映射的键条目在类A中搜索函数。接下来使用地图的值条目搜索B类的功能。调用(调用)两者并比较输出。
HashMap<String,String> mymap=new HashMap<>();
mymap.put("intA","intB");
mymap.put("boolA","boolB");
for(Map.Entry<String,String> e:mymap.entrySet()) {
// names not ok, maybe take a bean helper class
Method m1=a.getClass().getMethod("get"+e.getKey()); // or look for fields if you dont have getters
Method m2=b.getClass().getMethod("get"+e.getValue());
Object r1=m1.invoke(a);
Object r2=m2.invoke(b);
if (!r1.equals(r2))
return false;
}
很抱歉没有真正的代码。必须添加空检查!
答案 10 :(得分:0)
public class Compare {
public static void main(String[] args) {
// TODO Auto-generated method stub
Compare compare= new Compare();
List<ClassA> myList = Arrays.asList(new ClassA(1, "A", false), new ClassA(2, "B", false));
Set<ClassB> mySet = new HashSet<ClassB>(Arrays.asList(new ClassB(1, "A", false), new ClassB(2, "B", false)));
System.out.println( compare.areEqual(myList,mySet));
}
public boolean areEqual(Collection<ClassA> colA,Collection<ClassB> colB){
boolean equal =false;
if(colA.size()!=colB.size()){
return equal;
}
Set<Integer> setA=new HashSet<Integer>();
Set<Integer> setB= new HashSet<Integer>();
for(ClassA obj : colA){
setA.add(obj.hashCode());
}
for(ClassB obj : colB){
setB.add(obj.hashCode());
}
if(setA.equals(setB)){
equal=true;
}
return equal;
}
}
class ClassA {
private int intA;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + intA;
result = prime * result + ((strA == null) ? 0 : strA.hashCode());
return result;
}
private String strA;
private boolean boolA;
// Constructor
public ClassA(int intA, String strA, boolean boolA) {
this.intA = intA;
this.strA = strA;
this.boolA = boolA;
} // Getters and setters etc. below...
}
class ClassB {
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + intB;
result = prime * result + ((strB == null) ? 0 : strB.hashCode());
return result;
}
private int intB;
private String strB;
private boolean boolB;
// Constructor
public ClassB(int intB, String strB, boolean boolB) {
this.intB = intB;
this.strB = strB;
this.boolB = boolB;
} // Getters and setters etc. below...
}
我覆盖这两个类的哈希码方法,以基于int和str创建哈希码,并创建一个方法来创建Intergers集合,如果你不想要哈希码,整数就是每个类的哈希码覆盖让我知道也会更新
答案 11 :(得分:0)
愿它有所帮助......
class ClassA {
private int intA;
private String strA;
private boolean boolA;
// Constructor
public ClassA(int intA, String strA, boolean boolA) {
this.intA = intA;
this.strA = strA;
this.boolA = boolA;
} // Getters and setters etc. below...
@Override
public boolean equals(Object obj) {
if (obj instanceof ClassA) {
ClassA obj2 = (ClassA) obj;
return (this.intA == obj2.intA && this.strA.equals(obj2.strA) && this.boolA == obj2.boolA);
} else {
ClassB obj2 = (ClassB) obj;
return (this.intA == obj2.intB && this.strA.equals(obj2.strB) && this.boolA == obj2.boolB);
}
}
@Override
public int hashCode() {
int hash = 3;
hash = 71 * hash + this.intA;
hash = 71 * hash + Objects.hashCode(this.strA);
hash = 71 * hash + (this.boolA ? 1 : 0);
return hash;
}
}
class ClassB {
private int intB;
private String strB;
private boolean boolB;
// Constructor
public ClassB(int intB, String strB, boolean boolB) {
this.intB = intB;
this.strB = strB;
this.boolB = boolB;
} // Getters and setters etc. below...
@Override
public boolean equals(Object obj) {
if (obj instanceof ClassB) {
ClassB obj2 = (ClassB) obj;
return (this.intB == obj2.intB && this.strB.equals(obj2.strB) && this.boolB == obj2.boolB);
} else {
ClassA obj2 = (ClassA) obj;
return (this.intB == obj2.intA && this.strB.equals(obj2.strA) && this.boolB == obj2.boolA);
}
}
@Override
public int hashCode() {
int hash = 5;
hash = 79 * hash + this.intB;
hash = 79 * hash + Objects.hashCode(this.strB);
hash = 79 * hash + (this.boolB ? 1 : 0);
return hash;
}
}
public void test() {
List<Object> myList = Arrays.asList(new ClassA(1, "A", true),
new ClassA(1, "A", true));
System.out.println(myList.get(0).equals(myList.get(1)));
}
答案 12 :(得分:0)
对于两个单个元素,明确定义了等效比较,对于集合,可以进行等效比较的几个变体。一个方面是是否考虑元素排序。此外,当排序不重要时,等效元素的基数(匹配数)可能会或可能不会很重要。
因此,建议使用与EquivalenceComparisonBuilder
一起使用的EquivalenceComparator
和ComparisonType
ComparisonType.ORDERING
ComparisonType.DUPLICATES
进行严格排序,{{1对于严格匹配计数和ComparisonType.SIMPLE
进行松散等价比较,其中一个集合中的每个元素至少是另一个集合中的一个等效元素就足够了。
请注意,如果集合可能包含EquivalenceComparator
个元素,null
的实现需要考虑null
个参数。
package equivalence;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.Predicate;
public class Equivalence {
public static interface EquivalenceComparison<S, T> {
boolean equivalent();
}
public static interface EquivalenceComparator<S, T> {
boolean equivalent(S s, T t);
}
static public class EquivalenceComparisonBuilder<S, T> {
enum ComparisonType {
ORDERING, DUPLICATES, SIMPLE
};
private Collection<S> ss;
private Collection<T> ts;
private EquivalenceComparator<S, T> ec;
private ComparisonType comparisonType;
public EquivalenceComparisonBuilder<S, T> setCollections(Collection<S> ss, Collection<T> ts) {
this.ss = ss;
this.ts = ts;
return this;
}
public EquivalenceComparisonBuilder<S, T> setEquivalenceComparator(EquivalenceComparator<S, T> ec) {
this.ec = ec;
return this;
}
public EquivalenceComparisonBuilder<S, T> setComparisonType(ComparisonType comparisonType) {
this.comparisonType = comparisonType;
return this;
}
public EquivalenceComparison<S, T> comparison() {
if (comparisonType == null || ss == null || ts == null) {
throw new NullPointerException();
}
switch (comparisonType) {
case ORDERING:
return new OrderingComparison<S, T>(ss, ts, ec);
case DUPLICATES:
return new DuplicatesComparison<S, T>(ss, ts, ec);
case SIMPLE:
return new SimpleComparison<S, T>(ss, ts, ec);
default:
throw new IllegalArgumentException("Unknown comparison type");
}
}
}
private static <S, T> EquivalenceComparator<T, S> mirrored(EquivalenceComparator<S, T> ec) {
return new EquivalenceComparator<T, S>() {
@Override
public boolean equivalent(T t, S s) {
return ec.equivalent(s, t);
}
};
}
private static class EquivalencePredicate<S, T> implements Predicate<T> {
private S s;
private EquivalenceComparator<S, T> equivalenceComparator;
public EquivalencePredicate(S s, EquivalenceComparator<S, T> equivalenceComparator) {
this.s = s;
this.equivalenceComparator = equivalenceComparator;
}
@Override
public boolean evaluate(T t) {
return equivalenceComparator.equivalent(s, t);
}
}
static private class OrderingComparison<S, T> implements EquivalenceComparison<S, T> {
private Collection<S> ss;
private Collection<T> ts;
private EquivalenceComparator<S, T> ec;
public OrderingComparison(Collection<S> ss, Collection<T> ts, EquivalenceComparator<S, T> ec) {
this.ss = ss;
this.ts = ts;
this.ec = ec;
}
@Override
public boolean equivalent() {
if (ss.size() != ts.size()) {
return false;
}
List<S> ssl = new ArrayList<S>(ss);
List<T> tsl = new ArrayList<T>(ts);
for (int i = 0; i < ssl.size(); i++) {
S s = ssl.get(i);
T t = tsl.get(i);
if (!ec.equivalent(s, t)) {
return false;
}
}
return true;
}
}
static private class DuplicatesComparison<S, T> implements EquivalenceComparison<S, T> {
private Collection<S> ss;
private Collection<T> ts;
private EquivalenceComparator<S, T> ec;
public DuplicatesComparison(Collection<S> ss, Collection<T> ts, EquivalenceComparator<S, T> ec) {
this.ss = ss;
this.ts = ts;
this.ec = ec;
}
@Override
public boolean equivalent() {
if (ss.size() != ts.size()) {
return false;
}
for (S s : ss) {
Collection<T> matchingTs = CollectionUtils.select(ts, new EquivalencePredicate(s, ec));
if (matchingTs.size() == 0) {
return false;
}
T t = matchingTs.iterator().next();
Collection<S> matchingSs = CollectionUtils.select(ss, new EquivalencePredicate(t, mirrored(ec)));
if (matchingTs.size() != matchingSs.size()) {
return false;
}
}
return true;
}
}
static private class SimpleComparison<S, T> implements EquivalenceComparison<S, T> {
private Collection<S> ss;
private Collection<T> ts;
private EquivalenceComparator<S, T> ec;
public SimpleComparison(Collection<S> ss, Collection<T> ts, EquivalenceComparator<S, T> ec) {
this.ss = ss;
this.ts = ts;
this.ec = ec;
}
@Override
public boolean equivalent() {
for (S s : ss) {
if (!CollectionUtils.exists(ts, new EquivalencePredicate(s, ec))) {
return false;
}
}
for(T t :ts) {
if (!CollectionUtils.exists(ss, new EquivalencePredicate(t, mirrored(ec)))) {
return false;
}
}
return true;
}
}
}
以下是一些测试用例:
package equivalence;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.junit.Assert;
import org.junit.Test;
import equivalence.Equivalence.EquivalenceComparator;
import equivalence.Equivalence.EquivalenceComparisonBuilder;
import equivalence.Equivalence.EquivalenceComparisonBuilder.ComparisonType;
public class EquivalenceExample {
static class A {
private int ia;
private String is;
private long a;
public A(int ia, String is, long a) {
this.ia = ia;
this.is = is;
this.a = a;
}
public int getIa() {
return ia;
}
public String getIs() {
return is;
}
public long getA() {
return a;
}
}
static class B {
private int ib;
private String is;
private long b;
public B(int ib, String is, long b) {
this.ib = ib;
this.is = is;
this.b = b;
}
public int getIb() {
return ib;
}
public String getIs() {
return is;
}
public long getB() {
return b;
}
}
static class ABEquivalenceComparator implements EquivalenceComparator<A, B> {
static public ABEquivalenceComparator INSTANCE = new ABEquivalenceComparator();
@Override
public boolean equivalent(A a, B b) {
return new EqualsBuilder().append(a.getIa(), b.getIb()).append(a.getIs(), b.getIs()).isEquals();
}
}
@Test
public void thatOrderingEquivalenceMatchesEquivalentElementsWhenInSameOrder() {
List<A> as = Arrays.asList(new A(1, "1", 99l), new A(2, "2", 98l));
LinkedHashSet<B> bs = new LinkedHashSet<B>(Arrays.asList(new B(1, "1", 99l), new B(2, "2", 99l)));
Assert.assertTrue(new EquivalenceComparisonBuilder<A, B>().setCollections(as, bs)
.setEquivalenceComparator(ABEquivalenceComparator.INSTANCE).setComparisonType(ComparisonType.ORDERING)
.comparison().equivalent());
}
@Test
public void thatOrderingEquivalenceDoesNotMatchEquivalentElementsWhenNotSameOrdering() {
List<A> as = Arrays.asList(new A(1, "1", 99l), new A(2, "2", 98l));
LinkedHashSet<B> bs = new LinkedHashSet<B>(Arrays.asList(new B(2, "2", 99l), new B(1, "1", 99l)));
Assert.assertFalse(new EquivalenceComparisonBuilder<A, B>().setCollections(as, bs)
.setEquivalenceComparator(ABEquivalenceComparator.INSTANCE).setComparisonType(ComparisonType.ORDERING)
.comparison().equivalent());
}
@Test
public void thatOrderingEquivalenceDoesNotMatchNonEquivalentElements() {
List<A> as = Arrays.asList(new A(1, "1", 99l), new A(2, "2", 98l));
LinkedHashSet<B> bs = new LinkedHashSet<B>(Arrays.asList(new B(1, "1", 99l), new B(1, "1", 99l)));
Assert.assertFalse(new EquivalenceComparisonBuilder<A, B>().setCollections(as, bs)
.setEquivalenceComparator(ABEquivalenceComparator.INSTANCE).setComparisonType(ComparisonType.ORDERING)
.comparison().equivalent());
}
@Test
public void thatDuplicatesEquivalenceMatchesEquivalentElementsRegardlessOrder() {
List<A> as = Arrays.asList(new A(1, "1", 99l), new A(2, "2", 98l));
LinkedHashSet<B> bs = new LinkedHashSet<B>(Arrays.asList(new B(2, "2", 99l), new B(1, "1", 99l)));
Assert.assertTrue(new EquivalenceComparisonBuilder<A, B>().setCollections(as, bs)
.setEquivalenceComparator(ABEquivalenceComparator.INSTANCE).setComparisonType(ComparisonType.DUPLICATES)
.comparison().equivalent());
}
@Test
public void thatDuplicatesEquivalenceDoesNotMatchesWhenElementsCardinlityDoNotMatch() {
List<A> as = Arrays.asList(new A(1, "1", 99l), new A(2, "2", 98l), new A(1, "1", 99l));
LinkedHashSet<B> bs = new LinkedHashSet<B>(
Arrays.asList(new B(2, "2", 99l), new B(1, "1", 99l), new B(2, "2", 99l)));
Assert.assertFalse(new EquivalenceComparisonBuilder<A, B>().setCollections(as, bs)
.setEquivalenceComparator(ABEquivalenceComparator.INSTANCE).setComparisonType(ComparisonType.DUPLICATES)
.comparison().equivalent());
}
@Test
public void thatSimpleEquivalenceMatchesRegardlessEquivalentElementCardinality() {
List<A> as = Arrays.asList(new A(1, "1", 99l), new A(2, "2", 98l), new A(1, "1", 99l));
LinkedHashSet<B> bs = new LinkedHashSet<B>(
Arrays.asList(new B(2, "2", 99l), new B(1, "1", 99l), new B(2, "2", 99l)));
Assert.assertTrue(new EquivalenceComparisonBuilder<A, B>().setCollections(as, bs)
.setEquivalenceComparator(ABEquivalenceComparator.INSTANCE).setComparisonType(ComparisonType.SIMPLE)
.comparison().equivalent());
}
@Test
public void thatSimpleEquivalenceMatchesRegardlessElementsCount() {
List<A> as = Arrays.asList(new A(1, "1", 99l), new A(2, "2", 98l));
LinkedHashSet<B> bs = new LinkedHashSet<B>(
Arrays.asList(new B(2, "2", 99l), new B(1, "1", 99l), new B(2, "2", 99l)));
Assert.assertTrue(new EquivalenceComparisonBuilder<A, B>().setCollections(as, bs)
.setEquivalenceComparator(ABEquivalenceComparator.INSTANCE).setComparisonType(ComparisonType.SIMPLE)
.comparison().equivalent());
}
@Test
public void thatSimpleEquivalenceDoesMatchesWhenElementsDoNotMatch() {
List<A> as = Arrays.asList(new A(1, "1", 99l), new A(2, "2", 98l));
LinkedHashSet<B> bs = new LinkedHashSet<B>(Arrays.asList(new B(2, "2", 99l), new B(3, "3", 99l)));
Assert.assertFalse(new EquivalenceComparisonBuilder<A, B>().setCollections(as, bs)
.setEquivalenceComparator(ABEquivalenceComparator.INSTANCE).setComparisonType(ComparisonType.SIMPLE)
.comparison().equivalent());
}
}