首页 > 解决方案 > 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 如何建立散列以及比较器将如何影响这一点的深刻理解。

标签: javacomparatorhashsettreeset

解决方案


对于HashMapand 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);
}

对于TreeMapand TreeSet,相等的概念基于比较(无论是类实现Comparable,还是您提供自定义Comparator)。在您提供的代码中,您有一个Comparator只考虑 s 的自定义name,因此它会认为任何两个Node相同的 sname是相等的,而不管它们的price.


推荐阅读