【问题标题】:Null Object in HashSet implementationHashSet 实现中的空对象
【发布时间】:2012-10-10 22:06:34
【问题描述】:

在Java API中,HashSet的实现是使用一个Object作为内部HashMap的值,

   // Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

但是HashMap允许它的值为null。我认为这不是填充值所必需的,那为什么需要呢?

【问题讨论】:

    标签: java hashset


    【解决方案1】:

    因为HashSet 合约指定remove() 返回true 如果指定的对象存在并被删除。为此,它使用包装的 HashMap#remove() 返回删除的值。

    如果您要存储null 而不是对象,那么对HashMap#remove() 的调用将返回null,这与尝试删除不存在的对象的结果没有区别,并且合同HashSet.remove() 无法实现。

    【讨论】:

      【解决方案2】:

      但是HashMap允许它的值为null

      当值完全由HashSet 控制时,这有什么关系?这确保与键关联的唯一值是PRESENT。因此,如果map.put 返回null,那可能只是因为之前没有该键的条目。

      值就在那里,因为必须指定 some 值,如果值 指定为 null,那就不好了 - 它会成功在调用add 之前很难判断是否有值。如果您要指定任何非空值,您不妨强制它始终为相同的值 - 例如,您不希望它阻止垃圾回收。

      现在,如果您要问为什么 HashSet 是根据 HashMap 实现的,而不是一个更有效的实现,它根本不记录值,那是另一个问题,还有一个我没有答案。

      【讨论】:

      • 嗯,现在我想起来了,我也想知道为什么HashSet 是按照HashMap 来实现的……我本以为会有更好的方法来做到这一点。
      • 我的理解是他们打算改变它......但从来没有解决它。不过,公平地说,如果您只是从条目中删除 value 字段并保持其余实现不变,那么出于对齐原因,您实际上不会节省任何速度或内存。你需要一个明显不同的实现来开始真正击败 HashMap 包装器实现,我猜他们从来没有这样做过?
      • 对齐参数是否肯定适用于 x86 和 x64,有和没有压缩 oops?如果是这样,我想这可以解释它......但它仍然感觉很奇怪。
      【解决方案3】:

      在 Java HashMap 中,从对象到 null 的映射与映射中根本不存在的对象相同。考虑:

      Object exists = new Object();
      map.put(exists, null);
      System.out.println(map.contains(exists)) // "true"
      System.out.println(map.get(exists)) // "null"
      Object notMapped = new Object();
      System.out.println(map.contains(notMapped)) // "false"
      System.out.println(map.get(notMapped)) // "null"
      

      此外,HashMap.put() 使用您放置的键返回旧值,在您的情况下为 null(因为该键不在地图中,或者它的值为 null)。

      【讨论】:

        【解决方案4】:

        Map,如果你打电话给put(key, null),你是分不出来的

        1. 密钥已经存在,映射到null
        2. 该键没有映射

        由于HashSetadd 委托给HashMap.put,因此PRESENT 需要履行Set.add 的合约,如果Set 中已经存在该对象,则返回false

        return map.put(e, PRESENT)==null; 
        

        【讨论】:

          【解决方案5】:

          我想再补充一点:

          因为,HashSet add() 方法的工作原理如下:

          公共布尔加法(E e) {

              return map.put(e, PRESENT)==null;
          }
          
          1. 假设,如果 PRESENT==null 那么当我们第一次在 HashMap 中添加项目时,它返回 null

            对象存在 = new Object();

                    V  value=  map.put(exists,null);
                        value will be null here
            

            HashSet 将返回 -> null == null ->> true

          2. 第二次我们在hashMap中添加相同的key,值为Null

             map.put(exists,null);
            

            返回 null == null ->> true 它将允许 hashSet 中的重复项。这就是 JDK 开发人员编写 PRESENT 对象的原因

          【讨论】:

            【解决方案6】:

            注意==null 部分......

            【讨论】:

              猜你喜欢
              • 2015-09-07
              • 2014-03-05
              • 1970-01-01
              • 1970-01-01
              • 2014-07-25
              • 1970-01-01
              • 1970-01-01
              • 2023-03-07
              • 1970-01-01
              相关资源
              最近更新 更多