开发者

Java如何根据key值修改Hashmap中的value值

开发者 https://www.devze.com 2023-03-23 10:38 出处:网络 作者: 学习路上的行人
目录根据key值修改Hashmap的value值HashMap的key更改后能否正确获取value?测试案例总之最后根据key值修改Hashmap的val开发者_开发教程ue值
目录
  • 根据key值修改Hashmap的value值
  • HashMap的key更改后能否正确获取value?
    • 测试案例
  • 总之
    • 最后

      根据key值修改Hashmap的val开发者_开发教程ue值

      如果原来map中没有key,会创建,如果原来有key,会使用value 覆盖掉原来的值

      map.put(key,value);

      这个实现对原值加一(前提是有这个key)

      map.put(key,map.get(key)+1);

      以下可以获取key对应的value,如果没有可以返回默认的value

      map.getOrDefault(key,value);

      HashMap的key更改后能否正确获取value?

      在HashMap 中存放的一系列键值对,其中键为某个我们自定义的类型。放入 HashMap 后,我们在外部把某一个 key 的属性进行更改,然后我们再用这个 key 从 HashMap 里取出元素,这时候 HashMap 会返回什么?

      我们办公室几个人答案都不一致,有的说返回null,有的说能正常返回value。但不论答案是什么都没有确凿的理由。我觉得这个问题挺有意思的,就写了代码测试。结果是返回null。需要说明的是我们自定义的类重写了 hashCode 方法。我想这个结果还是有点意外的,因为我们知道 HashMap 存放的是引用类型,我们在外面把 key 更新了,那也就是说 HashMap 里面的 key 也更新了,也就是这个 key 的 hashCode 返回值也会发生变化。这个时候 key 的 hashCode 和 HashMap 对于元素的 hashCode 肯定一样,equals也肯定返回true,因为本来就是同一个对象,那为什么不能返回正确的值呢?

      测试案例

      这里有 2 个案例,一个是 Person 类,还有一个是 Student 类,我们来验证下以上的观点(附带结论):

      • 修改了对象属性是否会改变它的 hashcode => 是的
      • 在 HashMap 里存取的时候是否会受到修改属性影响取值 => 取值为 null
      package tech.luxsun.interview.luxinterviewstarter.collection;
      
      import lombok.AllArgsConstructor;
      import lombok.Data;
      import lombok.NoArgsConstrhttp://www.devze.comuctor;
      import Java.phputil.HashMap;
      
      /**
      * @author Lux Sun
      * @date 2021/4/22
      */
      public class MapDemo0 {
      
        public static void main(String[] args) {
          HashMap<Object, Object> map = new HashMap<>();
      
          // Person Case
          Person p = new Person("Bob", 12);
          map.put(p, "person");
          System.out.println(p.hashCode());
          System.out.println(map.get(p));
      
          p.setAge(13);
          System.out.println(p.hashCode()编程);
          System.out.println(map.get(p));
      
          // Student Case
          Student stu = new Student("Bob", 12);
          map.put(stu, "student");
          System.out.println(stu.hashCode());
          System.out.println(map.get(stu));
      
          stu.setAge(13);
          System.out.println(stu.hashCode());
          System.out.println(map.get(stu));
        }
      }
      
      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      class Person {
        private String name;
        private Integer age;
      
        public int hashCode() {
          return 123456;
        }
      }
      
      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      class Student {
        private String name;
        private Integer age;
      }

      输出结果

      123456

      person

      123456

      person

      71154

      student

      71213

      null

      源码

      hashCode 源码

      public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $age = this.getAge();
        int result = result * 59 + ($age == null ? 43 : $age.hashCode());
        Object $name = this.getName();
        result = result * 59 + ($name == null ? 43 : $name.hashCode());
        return result;
      }

      map.get 源码

      /**
      * Returns the value to which the specified key is mapped,
      * or {@code null} if this map contains no mapping for the key.
      *
      * <p>More formally, if this map contains a mapping from a key
      * {@code k} to a value {@code v} such that {@code (key==null ? k==null :
      * key.equals(k))}, then this method returns {@code v}; otherwise
      * it returns {@code null}. (There can be at most one such mapping.)
      *
      * <p>A return value of {@code null} does not <i>necessarily</i>
      * indicate that the map contains no mapping for the key; it's also
      * possible that the map explicitly maps the key to {@code null}.
      * The {@link #containsKey containsKey} operation may be used to
      * distinguish these two cases.
      *
      * @see #put(Object, Object)
      */
      public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
      }
      
      
      /**
      * Computes key.hashCode() and spreads (XORs) higher bits of hash
      * to lower. Because the table uses power-of-two masking, sets of
      * hashes that vary only in bits above the current mask will
      * always collide. (Among known examples are sets of Float keys
      * holding consecutive whole numbers in small tables.) So we
      * apply a transform that spreads the impact of higher bits
      * downward. There is a tradeoff between speed, utility, and
      * quality of bit-spreading. Because many common sets of hashes
      * are already reasonably distributed (so don't benefit from
      * spreading), and because we use trees to handle large sets of
      * collisions in bins, we just XOR some shifted bits in the
      * cheapest possible way to reduce systematic lossage, as well as
      * to incorporate impact of the highest bits that would otherwise
      * never be used in index calculations because of table bounds.
      */
      static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
      }
      
      /**
      * Implements Map.get and related methods
      *
      * @param hash hash for key
      * @param key the key
      * @return the node, or null if none
      */
      final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
          (first = tab[(n - 1) & hash]) != null) {
          if (first.hash == hash && // always check first node
            ((k = first.key) == key || (key != null && key.equals(k))))
            return first;
          if ((e = first.next) != null) {
            if (first instanceof TreeNode)
              return ((TreeNode<K,V>)first).getTreeNode(hash, key);
            do {
              if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
            } while ((e = e.next) != null);
          }
        }
        return null;
      }

      总之

      可以看到先取得了一个table,这个table实际上是个数组。然后在table里面找对应 key 的value。找的标准就是hash等于传入参数的hash, 并且满足另外两个条件之一:k = e.key,也就是说他们是同一个对象,或者传入的 key 的equal目标的 key 。我们的问题出在那个hash(key.hashCode()),可以看到 HashMap 在存储元素时是把 key 的 hashCode 再做了一次hash。得到的hash将最终作为编程客栈元素存储位置的依据。对应到我们的情况:第一次存储时,hash函数采用key.hashCode作为参数得到了一个值,然后根据这个值把元素存到了某个位置。

      当我们再去取元素的时候,key.hashCode的值已经出现了变化,所以这里的hash函数结果也发生了变化,所以当它尝试去获得这个 key 的存储位置时就不能得到正确的值,导致最终找不到目标元素。要想能正确返回,很简单,把Person类的 hashCode 方法改一下,让它的 hashCode 不依赖我们要修改的属性,但实际开发中肯定不能这么干,我们总是希望当两个对象的属性不完全相同时能返回不同的 hashCode 值。

      所以结论就是当把对象放到 HashMap 后,不要去修改 key 的属性,除非你重写了该实体类的 hwww.devze.comashCode 方法不受属性限制。

      最后

      以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

      0

      精彩评论

      暂无评论...
      验证码 换一张
      取 消

      关注公众号