【问题标题】:HashSet allows multiple items with same HashCodeHashSet 允许多个项目具有相同的 HashCode
【发布时间】:2019-06-03 17:41:56
【问题描述】:

我的 HashSet 包含多个具有相同 HashCode 的“AccessRequests”。我只希望有一个实例。我认为具有相同 HashCode 的项目不会出现在 HashSet 中。我在这里做错了什么?

更新:基于 HashSet 仅在列表中保留一个不等于另一个项目的假设,并且我的 equals/hash 方法可能需要简化,我已经更新了我的问题。我仍然在我的 HashSet 中获得多个评估为 Equals 的项目。

以下是“AccessRequest”中的 HashCode 和 Equals 方法

更新:我更新了我的哈希和等于,只有我需要“等于”的必要字段

    @Override
public int hashCode() {
    int hash = 5;
    hash = 79 * hash + Objects.hashCode(this.targets);
    hash = 79 * hash + Objects.hashCode(this.sources);
    hash = 79 * hash + Objects.hashCode(this.destinations);
    hash = 79 * hash + Objects.hashCode(this.services);
    hash = 79 * hash + Objects.hashCode(this.action);
    return hash;
}

@Override
public boolean equals(Object obj) {
    if (this == obj) {
        return true;
    }
    if (obj == null) {
        return false;
    }
    if (getClass() != obj.getClass()) {
        return false;
    }
    final AccessRequest other = (AccessRequest) obj;
    if (!Objects.equals(this.action, other.action)) {
        return false;
    }
    if (!Objects.equals(this.targets, other.targets)) {
        return false;
    }
    if (!Objects.equals(this.sources, other.sources)) {
        return false;
    }
    if (!Objects.equals(this.destinations, other.destinations)) {
        return false;
    }
    if (!Objects.equals(this.services, other.services)) {
        return false;
    }
    return true;
}

创建 AccessRequest 后,我​​将它们转储到 HashSet 并迭代: 我的 HashSet 定义如下:

 Set<AccessRequest> ars = new HashSet();

       ArrayList<AccessRequest> arsAsList = new ArrayList(ars);
        for(int position=0;position<arsAsList.size();position++){
            AccessRequest fixedAR = arsAsList.get(position);
            ArrayList<AccessRequest> comparToList = new ArrayList(ars);
            for(int cPosition=0;cPosition<comparToList.size();cPosition++){
                AccessRequest nextAR = comparToList.get(cPosition);
                if(fixedAR.equals(nextAR)){
                    System.out.println("position= "+position+"  cPosition "+cPosition);
                }
            }
            System.out.println("\n Next AR");
        }

以下是输出:

position= 0  cPosition 0
position= 0  cPosition 5
position= 0  cPosition 6
position= 0  cPosition 14
position= 0  cPosition 24
position= 0  cPosition 32
position= 0  cPosition 39
position= 0  cPosition 40
position= 0  cPosition 43
position= 0  cPosition 77
position= 0  cPosition 96
position= 0  cPosition 97
position= 0  cPosition 99
position= 0  cPosition 109
position= 0  cPosition 111
position= 0  cPosition 115
position= 0  cPosition 173
position= 0  cPosition 182
position= 0  cPosition 187

【问题讨论】:

  • 是的,它允许有哈希码冲突,哈希表也会检查键是否相等。
  • 您在这里使用了大量的字段来为不相等的对象计算相同的哈希码(4 个不相等的对象具有相同的哈希码!)。每个字段是否有真正有用的 hashCode 实现?

标签: java hashset


【解决方案1】:

基于equals 方法 (1) 设置防止重复。来自javadoc(我强调):

不包含重复元素的集合。更正式地说,集合不包含一对元素 e1 和 e2 使得 e1.equals(e2),并且最多包含一个 null 元素。

如果您的元素根据其哈希码应该相等,则相应地实现equals 方法(例如,仅比较调用hashCode 的结果)。请注意,这可能不是最好的主意,因为您的 equals 方法当前会评估更多属性。

(1):至少是您当前使用的HashSet

【讨论】:

  • 使用 .equals 比较元素也会在我的 HashSet 中返回重复的结果
  • 你能告诉我们minimal reproducible example吗?或者至少有两个与您相同的 AccessRequest,它们的值所有,包括它们的哈希码?
【解决方案2】:

您刚才观察到的是哈希码冲突。因为哈希码函数将较大集合中的值(例如,所有可能的 String,它们的数量无限)映射到较小的集合(例如,所有可能的 int,只有 2^32 个不同的值),所以总是会发生冲突。

这就是为什么利用散列的数据结构总是处理散列冲突,例如通过实施Open Addressing。你可以看一下Hash Tables wiki, Collision resolution section 来更好地理解问题:

在对大量可能键的随机子集进行哈希处理时,哈希冲突实际上是不可避免的。例如,如果将 2,450 个密钥散列到一百万个桶中,即使具有完全均匀的随机分布,根据生日问题,至少有 95% 的机会将至少两个密钥散列到同一个槽中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-04
    • 2014-09-07
    • 1970-01-01
    • 1970-01-01
    • 2016-11-08
    • 1970-01-01
    相关资源
    最近更新 更多