how to insert into set without changing the equals

2019-05-25 19:32发布

问题:

I'm looking for a suggestion. I have a Person class with String firstName and String lastName When i'm tying to insert the list values with the same String like :

set.add(new Person("firstName","lastName"))

set.add(new Person("firstName","lastName"))

The set doesn`t filter the objects and they still getting in the set. There is any suggestion to create set list without overriding the equales and hashcode functions? Maybe with guava or some groovy list? Thanks, Or.

回答1:

You can create a TreeSet with your own Comparator.

Set<Person> set = new TreeSet<Person>(new Comparator<Person>() {
    @Override
    public int compare(Person p1, Person p2) {
        // Your own compare logic
    }
});


回答2:

In Guava there's an Equivalence class designed to such things. Create your own Equivalence class like this one:

import com.google.common.base.Equivalence;
import com.google.common.base.Objects;

public class PersonEquivalence extends Equivalence<Person> {

  @Override
  protected boolean doEquivalent(Person p1, Person p2) {
    return Objects.equal(p1.getFistName(), p2.getFistName())
        && Objects.equal(p1.getLastName(), p2.getLastName());
  }

  @Override
  protected int doHash(Person person) {
    return Objects.hashCode(person.getFistName(), person.getLastName());
  }

}

And then this code

Set<Equivalence.Wrapper<Person>> set = Sets.newHashSet();
PersonEquivalence personEquivalence = new PersonEquivalence();
set.add(personEquivalence.wrap(new Person("Joe", "Doe")));
set.add(personEquivalence.wrap(new Person("Joe", "Doe")));
set.add(personEquivalence.wrap(new Person("Jane", "Doe")));
System.out.println(set);

prints

[PersonEquivalence@8813f2.wrap(Person{firstName=Jane, lastName=Doe}),
 PersonEquivalence@8813f2.wrap(Person{firstName=Joe, lastName=Doe})]

Of course it's a bit verbose, but you can create ForwardingSet to automatically wrap and unwrap Persons for you.



回答3:

You can't, without violating the contract of Set. Either don't use a Set, or wrap the Person in another class that implements equals and hashcode based on the inner Person (see the other answer for a way to do this in Guava).



回答4:

Here's a rough attempt at my map suggestion.

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class PeopleCarrier implements Iterable<Person>{

    private Map<PersonKey, Person> storage = new HashMap<PersonKey, Person>();

    public void add(Person p) {
        PersonKey pk = new PersonKey(p);
        storage.put(pk, p);     
    }

    public boolean contains(Person p) {
        return storage.containsKey(new PersonKey(p));
    }

    @Override
    public Iterator<Person> iterator() {
        return new Iterator<Person>() {

            private Iterator<PersonKey> i = storage.keySet().iterator();

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }

            @Override
            public Person next() {
                return storage.get(i.next());               
            }

            @Override
            public boolean hasNext() {
                return i.hasNext();
            }
        };
    }

    private class PersonKey {
        private String firstname;
        private String lastname;
        public PersonKey(Person p) {
            this.firstname = p.getFirstname();
            this.lastname = p.getLastname();
        }
        /* (non-Javadoc)
         * @see java.lang.Object#hashCode()
         */
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + getOuterType().hashCode();
            result = prime * result
                    + ((firstname == null) ? 0 : firstname.hashCode());
            result = prime * result
                    + ((lastname == null) ? 0 : lastname.hashCode());
            return result;
        }
        /* (non-Javadoc)
         * @see java.lang.Object#equals(java.lang.Object)
         */
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (!(obj instanceof PersonKey))
                return false;
            PersonKey other = (PersonKey) obj;
            if (!getOuterType().equals(other.getOuterType()))
                return false;
            if (firstname == null) {
                if (other.firstname != null)
                    return false;
            } else if (!firstname.equals(other.firstname))
                return false;
            if (lastname == null) {
                if (other.lastname != null)
                    return false;
            } else if (!lastname.equals(other.lastname))
                return false;
            return true;
        }
        private PeopleCarrier getOuterType() {
            return PeopleCarrier.this;
        }   

    }
}