java - Java Set 移除“复杂对象”
问题描述
我总是对 Java Collections (set, map) 删除“复杂对象”感到困惑,我的意思是一些自定义类,而不仅仅是原始类型。
我正在尝试:
public class Main {
public static void main(String[] args) {
// set
Set<Node> set = new HashSet<>();
set.add(new Node(1,2));
set.add(new Node(3,4));
System.out.println(set);
set.remove(new Node(1,2));
System.out.println(set + "\n");
// tree set
TreeSet<Node> tset = new TreeSet<>((a, b) -> a.name - b.name);
tset.add(new Node(1,2));
tset.add(new Node(3,4));
System.out.println(tset);
tset.remove(new Node(1,2));
System.out.println(tset);
}
}
class Node {
int name;
int price;
Node(int name, int price) {
this.name = name;
this.price = price;
}
}
在上面的示例中,打印输出将是:
Set:
[Node@5ba23b66, Node@2ff4f00f]
[Node@5ba23b66, Node@2ff4f00f]
TreeSet:
[Node@48140564, Node@58ceff1]
[Node@58ceff1]
显然,一般的 Set 不能 remove new Node(1, 2)
,它被视为不同的对象。但有趣的是,TreeSet 可以删除,我认为这是因为哈希代码基于我在这里定义的 lambda 比较器?
如果我更改为 remove new Node(1, 6)
,有趣的是它是相同的打印输出,显然 TreeSet 中的 remove 仅基于名称值。
我认为我仍然缺乏对 Set 如何建立散列以及比较器将如何影响这一点的深刻理解。
解决方案
对于HashMap
and HashSet
,您需要覆盖hashCode()
and equals(Object)
,如果两个对象相等,则它们应该具有相等的哈希码。例如,在您的情况下,您可以像这样实现它:
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) {
return false;
}
Node node = (Node) o;
return name == node.name && price == node.price;
}
@Override
public int hashCode() {
return Objects.hash(name, price);
}
对于TreeMap
and TreeSet
,相等的概念基于比较(无论是类实现Comparable
,还是您提供自定义Comparator
)。在您提供的代码中,您有一个Comparator
只考虑 s 的自定义name
,因此它会认为任何两个Node
相同的 sname
是相等的,而不管它们的price
.
推荐阅读
- c# - C#泛型工厂模式没有说明泛型的类型
- big-o - 使用天花板/地板函数查找步数函数
- java - Spark SQL 配置单元表错误
- php - 在 Symfony 中键入提示 json 对象
- javascript - textarea 上对 null 的修剪检查停止工作
- html - 为什么 Heroku 上的应用程序视口比 localhost 上的小?
- javascript - 嵌套查询 mongoose .find
- c++ - C++ 端口监视器 (mfilemon.dll) RDP 虚拟通道代码未执行
- selenium - Selenium ChromeDriver:增加获取 Anchor 元素值的时间
- javascript - 打印 typescript 编译器选项