Java 设置删除 "complex object"
Java Set removes "complex object"
我总是对 Java 集合(集合、映射)删除“复杂对象”感到困惑,我指的是一些自定义的 class 而不仅仅是原始类型。
我正在试验:
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是不能移除new Node(1, 2)
的,它被当作一个不同的对象。但有趣的是,TreeSet 可以删除,我认为这是因为哈希代码基于我在此处定义的 lambda 比较器?
如果我更改为删除 new Node(1, 6)
,有趣的是它是相同的打印输出,显然 TreeSet 中的删除仅基于名称值。
我想我对 Set 如何构建散列以及比较器如何影响它仍然缺乏深入的了解。
javadoc 来拯救
https://docs.oracle.com/javase/7/docs/api/java/util/Set.html#remove(java.lang.Object)
Removes the specified element from this set if it is present (optional
operation). More formally, removes an element e such that (o==null ?
e==null : o.equals(e)), if this set contains such an element. Returns
true if this set contained the element (or equivalently, if this set
changed as a result of the call). (This set will not contain the
element once the call returns.)
所以只需更改您的节点 class 并用您自己的方法覆盖 equals(Object o) 方法。
对于HashMap
和HashSet
,您需要覆盖hashCode()
和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
和 TreeSet
,相等的概念基于比较(无论 class 实现 Comparable
,还是您提供自定义 Comparator
).在您提供的代码中,您有一个自定义 Comparator
,它只考虑 name
,因此它会将具有相同 name
的任何两个 Node
视为相等,无论他们 price
.
我总是对 Java 集合(集合、映射)删除“复杂对象”感到困惑,我指的是一些自定义的 class 而不仅仅是原始类型。
我正在试验:
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是不能移除new Node(1, 2)
的,它被当作一个不同的对象。但有趣的是,TreeSet 可以删除,我认为这是因为哈希代码基于我在此处定义的 lambda 比较器?
如果我更改为删除 new Node(1, 6)
,有趣的是它是相同的打印输出,显然 TreeSet 中的删除仅基于名称值。
我想我对 Set 如何构建散列以及比较器如何影响它仍然缺乏深入的了解。
javadoc 来拯救
https://docs.oracle.com/javase/7/docs/api/java/util/Set.html#remove(java.lang.Object)
Removes the specified element from this set if it is present (optional operation). More formally, removes an element e such that (o==null ? e==null : o.equals(e)), if this set contains such an element. Returns true if this set contained the element (or equivalently, if this set changed as a result of the call). (This set will not contain the element once the call returns.)
所以只需更改您的节点 class 并用您自己的方法覆盖 equals(Object o) 方法。
对于HashMap
和HashSet
,您需要覆盖hashCode()
和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
和 TreeSet
,相等的概念基于比较(无论 class 实现 Comparable
,还是您提供自定义 Comparator
).在您提供的代码中,您有一个自定义 Comparator
,它只考虑 name
,因此它会将具有相同 name
的任何两个 Node
视为相等,无论他们 price
.