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) 方法。

对于HashMapHashSet,您需要覆盖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);
}

对于 TreeMapTreeSet,相等的概念基于比较(无论 class 实现 Comparable,还是您提供自定义 Comparator).在您提供的代码中,您有一个自定义 Comparator,它只考虑 name,因此它会将具有相同 name 的任何两个 Node 视为相等,无论他们 price.