简体   繁体   中英

Java Set removes “complex object”

I'm always confused about the Java Collections (set, map) remove "complex object", which I mean some self-defined class rather than just primitive type.

I'm experimenting like:

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;
    }
}

In the example above, the printout would be:

 Set:
[Node@5ba23b66, Node@2ff4f00f]
[Node@5ba23b66, Node@2ff4f00f]

 TreeSet:
[Node@48140564, Node@58ceff1]
[Node@58ceff1]

Obviously, the general Set can't remove new Node(1, 2) , which is treated as a different object. But interestingly the TreeSet can remove, which I think because the hashing code is based on the lambda comparator I defined here?

And if I change to remove new Node(1, 6) , interestingly it's the same printout, where obviously the remove in TreeSet is based on only the name value.

I think I still lack of deep understanding of how Set build up hashing and how comparator would affect this.

For HashMap and HashSet , you need to overwrite hashCode() and equals(Object) , where if two objects are equal, they should have equal hash codes. Eg, in your case, you could implement it like this:

@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);
}

For TreeMap and TreeSet , the notion of equality is based on a comparison (whether the class implements Comparable , or you supply a custom Comparator ). In the code you provided, you have a custom Comparator that only takes the name in to consideration, so it would consider any two Node s with the same name as being equal, regardless of their price .

javadoc comes to rescue

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.)

So just change your Node class and override equals(Object o) method with your own.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM