问题描述
假设我有两张地图:
HashMap<TestClass,Integer> map1,map2;
TestClass 被定义为:
class TestClass {
int i;
public TestClass(int i){
this.i = i;
}
}
以及以下代码来填充地图:
map1.put(new TestClass(1),1);
map1.put(new TestClass(2),2);
map2.put(new TestClass(1),1);
map2.put(new TestClass(2),2);
如果两个映射都包含“相同”的键和值,我想编写一个返回 true
的方法。 是的,在上面的示例中,我可以创建一个局部变量来存储第一个构造函数调用的结果并将其传递给第二个映射,但是在我的实际应用程序中不能这样做因为我想比较彼此不相等的相似对象,至少就 equals
方法的 java 实现而言。
我之前的实现(简化):
if(map1.size() == map2.size()){
for(String key1 : map1.keySet()){
if(!map1.get(key1).equals(map2.get(key1))
return false;
}
return true;
} else
return false;
这适用于 String
,因为即使在不同位置实例化两次,它们也彼此相等。
由于两次执行 TestClass 的构造函数会返回两个不同的对象,map2.get(key1)
将返回 null(或抛出异常,不完全确定),因为 key1
不在 map2
中。
为了比较两张地图,我编写了以下代码(简化):
if(map1.size() == map2.size()){
for(TestClass key1 : map1.keySet()){
boolean foundEqualing = false;
for (TestClass key2 : map2.keySet()) {
// Search equaling
if (key1.i == key2.i) {
// Check if corresponding values equal as well
if (map1.get(key1).equals(map2.get(key2))
// Store that a equaling key value pair was found
foundEqualing = true;
// Break if keys equaled each other
break;
}
}
// Return false if no equaling key was found or if keys equal each other but the corresponding values don't
if (!foundEqualing)
return false;
}
return true;
} else
return false;
我对这段代码的问题是它会遍历两个地图,这对我来说似乎效率很低。我不熟悉正确的符号,但如果我没记错的话,如果地图的大小加倍,操作所需的时间会翻四倍。
有没有比编写 for 循环更有效地循环或过滤这些映射的方法?
我的真实世界代码使用反射,因此不要太专注于提供的示例。映射的类型可以来自每一种类型(我唯一知道的是它们必须实现某个接口,否则它们就会被忽略)。
编辑:
我目前正在考虑使用流过滤器收集语法,但我从未使用过。无论如何这更有效还是只是在内部循环遍历地图?
解决方法
我想编写一个方法,如果两个映射都包含 “相同”的键和值。
...
有没有办法更有效地循环 以不同于编写 for 循环的方式通过或过滤这些映射?
只需覆盖 equals
中的 hashCode
和 TestClass
方法并按如下方式比较地图:
map1.equals(map2);
以下是AbstractMap#equals的摘录:
比较指定的对象与此映射是否相等。返回真 如果给定的对象也是一个地图并且这两个地图表示相同 映射。更正式地说,两个映射 m1 和 m2 表示相同的 映射如果 m1.entrySet().equals(m2.entrySet())。这确保了 equals 方法可以在不同的实现中正常工作 地图界面。
,如果您可以在 TestClass 中实现 equals
方法,那么这可以通过更简单的方式完成。您根本不必使用循环。您也可以将 equals
与地图一起使用。
Map.equals() 的工作方式是使用 Object.equals() 方法比较键和值。这意味着它只有在键和值对象都正确实现 equals() 时才有效。
import java.util.HashMap;
import java.util.Objects;
class Main {
public static void main(String[] args) {
HashMap<TestClass,Integer> map1,map2;
map1 = new HashMap<>();
map2 = new HashMap<>();
map1.put(new TestClass(1),1);
map1.put(new TestClass(2),2);
map2.put(new TestClass(1),1);
map2.put(new TestClass(2),2);
System.out.println(map1.equals(map2));
}
}
class TestClass {
int i;
public TestClass(int i){
this.i = i;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TestClass testClass = (TestClass) o;
return i == testClass.i;
}
@Override
public int hashCode() {
return Objects.hash(i);
}
}
编辑:如评论中所述,由于使用了反射,无法使用上述实现。通过进行通常的空间和时间交易,仍然可以在时间复杂度方面提高性能。
由于您无论如何都必须编写逻辑来比较两个对象的相等性,因此我创建了一个带有附加值字段的类并在那里实现了 equals
逻辑。我正在为这个类使用 HashSet,从本质上将时间复杂度从 O(m*n)
降低到 O(max(m,n))
。(假设大小为 m 和 n)。
import java.util.HashMap;
import java.util.HashSet;
import java.util.Objects;
class Main {
public static void main(String[] args) {
HashMap<TestClass,2);
boolean check = checkEqual(map1,map2);
System.out.println(check);
//---------------------------------------
map1 = new HashMap<>();
map2 = new HashMap<>();
map1.put(new TestClass(1),3);
check = checkEqual(map1,map2);
System.out.println(check);
}
private static boolean checkEqual(HashMap<TestClass,HashMap<TestClass,Integer> map2) {
HashSet<TestAndValue> set = new HashSet<>();
map1.forEach((k,v) -> set.add(new TestAndValue(k,v)));
for(TestClass t: map2.keySet()) {
if(!set.contains(new TestAndValue(t,map2.get(t))))
return false;
}
return true;
}
}
class TestAndValue {
TestClass t;
int val;
public TestAndValue(TestClass t,int val) {
this.t = t;
this.val = val;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TestAndValue that = (TestAndValue) o;
return val == that.val && t.i == that.t.i;
}
@Override
public int hashCode() {
return Objects.hash(t.i,val);
}
}
class TestClass {
int i;
public TestClass(int i){
this.i = i;
}
}
输出是:
true
false
虽然实现起来很乱,但我希望它能给你足够的想法在线性时间内实现它。