【问题标题】:how to insert into set without changing the equals and hashcode如何在不更改等号和哈希码的情况下插入集合
【发布时间】:2012-09-20 07:59:26
【问题描述】:

我正在寻找建议。 我有一个带有 String firstName 和 String lastName 的 Person 类 当我要插入具有相同字符串的列表值时:

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

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

集合不过滤对象,它们仍然进入集合。 有什么建议可以在不覆盖 equales 和 hashcode 函数的情况下创建集合列表? 也许有番石榴或一些时髦的清单? 谢谢, 或者。

【问题讨论】:

  • 覆盖equalshashCode有什么问题?
  • 我的类比 Person 例子更复杂。我正在使用 groovy,我有一个 groovy 类,它有 2 个域和更多变量,所以我不想改变我的相等,我只需要一次创建一些只依赖于我的类的 2 个字符串的集合。
  • 如果您提供的代码被执行,您希望结果是什么?两个人在集合?只有第一个人在集合?通常结果是只有第二个人留在集合中。
  • @ork 最后,如果以正确的方式进行操作,并获得equalshashcode 正确,您可能会好得多......人们会进一步跌倒该行,假设它们已正确实现,而不是您使用带有自定义比较器的 TreeSet

标签: java groovy set guava hashset


【解决方案1】:

在 Guava 中,有一个 Equivalence 类专为此类事情而设计。像这样创建自己的Equivalence 类:

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

}

然后是这段代码

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

打印

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

当然有点冗长,但你可以创建ForwardingSet 来自动为你包装和解开Persons。

【讨论】:

    【解决方案2】:

    您可以使用自己的比较器创建TreeSet

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

    【讨论】:

    • 一个聪明,但可能不是一个好主意。实现equals()hashcode(),如果你必须实现一些方法。仅仅使用TreeSet,当实际上不需要提供的功能时,是另一个坏主意。
    • @AdeelAnsari 这完全取决于上下文。如果它适合他的需要,那么我真的看不出问题。
    • 我也不是,它似乎很适合这个问题。如果您想要一个解决问题并“正确”使用集合的解决方案,那么您可以使用一个映射,该映射以一个仅包含名字和姓氏字段的新类为键。
    • @GlenLamb:来自文档“请注意,如果要正确实现 Set 接口,集合维护的顺序(无论是否提供显式比较器)必须与 equals 一致。”
    • @maba:确实,这就是你没有得到反对票的原因。因为不履行总合同是有效的,但不是一个好习惯。这正是我的观点。
    【解决方案3】:

    如果不违反Set 的合同,您不能这样做。要么不使用Set,要么将Person 包装在另一个基于内部Person 实现equalshashcode 的类中(有关在Guava 中执行此操作的方法,请参见其他答案)。

    【讨论】:

    • 最好说“你不应该”,而不是“你不能”。 +1
    【解决方案4】:

    这是我的地图建议的粗略尝试。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-03-13
      • 1970-01-01
      • 1970-01-01
      • 2011-07-09
      • 1970-01-01
      • 1970-01-01
      • 2020-12-12
      相关资源
      最近更新 更多