精华内容
下载资源
问答
  • hashMap实现原理

    万次阅读 多人点赞 2019-07-31 18:35:50
    此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。 2. HashMap的数据结构:  在java编程语言中,最基本的结构就是两种,一...

    1. HashMap概述:

      HashMap是基于哈希表的Map接口的非同步实现(Hashtable跟HashMap很像,唯一的区别是Hashtalbe中的方法是线程安全的,也就是同步的)。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

    2. HashMap的数据结构:

      在java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外。HashMap实际上是一个“链表的数组”的数据结构,每个元素存放链表头结点的数组,即数组和链表的结合体。

      从上图中可以看出,HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。源码如下:

    /**
     * The table, resized as necessary. Length MUST Always be a power of two.
     */
    transient Entry[] table;
    
    static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        final int hash;
        ……
    }

      可以看出,Entry就是数组中的元素,每个 Map.Entry 其实就是一个key-value对,它持有一个指向下一个元素的引用,这就构成了链表。

    3.    HashMap的存取实现:

      1) 存储:

    public V put(K key, V value) {

        // HashMap允许存放null键和null值。
        // 当key为null时,调用putForNullKey方法,将value放置在数组第一个位置。
        if (key == null)
            return putForNullKey(value);
        // 根据key的hashCode重新计算hash值。
        int hash = hash(key.hashCode());
        // 搜索指定hash值所对应table中的索引。
        int i = indexFor(hash, table.length);
        // 如果 i 索引处的 Entry 不为 null,通过循环不断遍历 e 元素的下一个元素。
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        // 如果i索引处的Entry为null,表明此处还没有Entry。
        // modCount记录HashMap中修改结构的次数
        modCount++;
        // 将key、value添加到i索引处。
        addEntry(hash, key, value, i);
        return null;
    }

      从上面的源代码中可以看出:当我们往HashMap中put元素的时候,先根据key的hashCode重新计算hash值,根据hash值得到这个元素在数组中的位置(即下标),如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。

      addEntry(hash, key, value, i)方法根据计算出的hash值,将key-value对放在数组table的 i 索引处。addEntry 是HashMap 提供的一个包访问权限的方法(就是没有public,protected,private这三个访问权限修饰词修饰,为默认的访问权限,用default表示,但在代码中没有这个default),代码如下:

    void addEntry(int hash, K key, V value, int bucketIndex) {

        // 获取指定 bucketIndex 索引处的 Entry 
        Entry<K,V> e = table[bucketIndex];
        // 将新创建的 Entry 放入 bucketIndex 索引处,并让新的 Entry 指向原来的 Entry
        table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
        // 如果 Map 中的 key-value 对的数量超过了极限
        if (size++ >= threshold)
        // 把 table 对象的长度扩充到原来的2倍。
            resize(2 * table.length);
    }

      当系统决定存储HashMap中的key-value对时,完全没有考虑Entry中的value,仅仅只是根据key来计算并决定每个Entry的存储位置。我们完全可以把 Map 集合中的 value 当成 key 的附属,当系统决定了 key 的存储位置之后,value 随之保存在那里即可。

      hash(int h)方法根据key的hashCode重新计算一次散列。此算法加入了高位计算,防止低位不变,高位变化时,造成的hash冲突。

    static int hash(int h) {

        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }

      我们可以看到在HashMap中要找到某个元素,需要根据key的hash值来求得对应数组中的位置。如何计算这个位置就是hash算法。前面说过HashMap的数据结构是数组和链表的结合,所以我们当然希望这个HashMap里面的 元素位置尽量的分布均匀些,尽量使得每个位置上的元素数量只有一个,那么当我们用hash算法求得这个位置的时候,马上就可以知道对应位置的元素就是我们要的,而不用再去遍历链表,这样就大大优化了查询的效率。

      对于任意给定的对象,只要它的 hashCode() 返回值相同,那么程序调用 hash(int h) 方法所计算得到的 hash 码值总是相同的。我们首先想到的就是把hash值对数组长度取模运算,这样一来,元素的分布相对来说是比较均匀的。但是,“模”运算的消耗还是比较大的,在HashMap中是这样做的:调用 indexFor(int h, int length) 方法来计算该对象应该保存在 table 数组的哪个索引处。indexFor(int h, int length) 方法的代码如下:

    static int indexFor(int h, int length) {

        return h & (length-1);
    }

      这个方法非常巧妙,它通过 h & (table.length -1) 来得到该对象的保存位,而HashMap底层数组的长度总是 2 的n 次方,这是HashMap在速度上的优化。在 HashMap 构造器中有如下代码:

    int capacity = 1;

        while (capacity < initialCapacity)
            capacity <<= 1;

      这段代码保证初始化时HashMap的容量总是2的n次方,即底层数组的长度总是为2的n次方。

      当length总是 2 的n次方时,h& (length-1)运算等价于对length取模,也就是h%length,但是&比%具有更高的效率。

      这看上去很简单,其实比较有玄机的,我们举个例子来说明:

      假设数组长度分别为15和16,优化后的hash码分别为8和9,那么&运算后的结果如下:

           h & (table.length-1)     hash    table.length-1       result

           8 & (15-1):              0100   &   1110      =        0100

           9 & (15-1):              0101   &   1110      =        0100

          ---------------------------------------------------------------

           8 & (16-1):              0100   &   1111      =        0100

           9 & (16-1):              0101   &   1111      =        0101

          ---------------------------------------------------------------

      从上面的例子中可以看出:当8、9两个数和(15-1)2=(1110)进行“与运算&”的时候,产生了相同的结果,都为0100,也就是说它们会定位到数组中的同一个位置上去,这就产生了碰撞,8和9会被放到数组中的同一个位置上形成链表,那么查询的时候就需要遍历这个链 表,得到8或者9,这样就降低了查询的效率。同时,我们也可以发现,当数组长度为15的时候,hash值会与(15-1)2=(1110)进行“与运算&”,那么最后一位永远是0,而0001,0011,0101,1001,1011,0111,1101这几个位置永远都不能存放元素了,空间浪费相当大,更糟的是这种情况中,数组可以使用的位置比数组长度小了很多,这意味着进一步增加了碰撞的几率,减慢了查询的效率!

      而当数组长度为16时,即为2的n次方时,2n-1得到的二进制数的每个位上的值都为1(比如(24-1)2=1111),这使得在低位上&时,得到的和原hash的低位相同,加之hash(int h)方法对key的hashCode的进一步优化,加入了高位计算,就使得有相同的hash值的两个值才会被放到数组中的同一个位置上形成链表

      所以说,当数组长度为2的n次幂的时候,不同的key算得得index相同的几率较小,那么数据在数组上分布就比较均匀,也就是说碰撞的几率小,相对的,查询的时候就不用遍历某个位置上的链表,这样查询效率也就较高了。

      根据上面 put 方法的源代码可以看出,当程序试图将一个key-value对放入HashMap中时,程序首先根据该 key的 hashCode() 返回值决定该 Entry 的存储位置:如果两个 Entry 的 key 的 hashCode() 返回值相同,那它们的存储位置相同。如果这两个 Entry 的 key 通过 equals 比较返回 true,新添加 Entry 的 value 将覆盖集合中原有Entry 的 value,但key不会覆盖。如果这两个 Entry 的 key 通过 equals 比较返回 false,新添加的 Entry 将与集合中原有 Entry 形成 Entry 链,而且新添加的 Entry 位于 Entry 链的头部——具体说明继续看 addEntry() 方法的说明。

      2) 读取:

    public V get(Object key) {

        if (key == null)
            return getForNullKey();
        int hash = hash(key.hashCode());
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
            e != null;
            e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                return e.value;
        }
        return null;
    }

      有了上面存储时的hash算法作为基础,理解起来这段代码就很容易了。从上面的源代码中可以看出:从HashMap中get元素时,首先计算key的hashCode,找到数组中对应位置的某一元素,然后通过key的equals方法在对应位置的链表中找到需要的元素。

      3) 归纳起来简单地说,HashMap 在底层将 key-value 当成一个整体进行处理,这个整体就是一个 Entry 对象。HashMap 底层采用一个 Entry[] 数组来保存所有的 key-value 对,当需要存储一个 Entry 对象时,会根据hash算法来决定其在数组中的存储位置,在根据equals方法决定其在该数组位置上的链表中的存储位置;当需要取出一个Entry时,也会根据hash算法找到其在数组中的存储位置,再根据equals方法从该位置上的链表中取出该Entry。

    4. HashMap的resize(rehash):

      当HashMap中的元素越来越多的时候,hash冲突的几率也就越来越高,因为数组的长度是固定的。所以为了提高查询的效率,就要对HashMap的数组进行扩容,数组扩容这个操作也会出现在ArrayList中,这是一个常用的操作,而在HashMap数组扩容之后,最消耗性能的点就出现了:原数组中的数据必须重新计算其在新数组中的位置,并放进去,这就是resize。

      那么HashMap什么时候进行扩容呢?当HashMap中的元素个数超过数组大小*loadFactor时,就会进行数组扩容,loadFactor的默认值为0.75,这是一个折中的取值。也就是说,默认情况下,数组大小为16,那么当HashMap中元素个数超过16*0.75=12(这个值就是代码中的threshold值,也叫做临界值)的时候,就把数组的大小扩展为 2*16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知HashMap中元素的个数,那么预设元素的个数能够有效的提高HashMap的性能。

    HashMap扩容的代码如下所示:

    //HashMap数组扩容
              void resize(int newCapacity) {
                    Entry[] oldTable = table;
                    int oldCapacity = oldTable.length;
                    //如果当前的数组长度已经达到最大值,则不在进行调整
                    if (oldCapacity == MAXIMUM_CAPACITY) {
                        threshold = Integer.MAX_VALUE;
                        return;
                    }
                    //根据传入参数的长度定义新的数组
                    Entry[] newTable = new Entry[newCapacity];
                    //按照新的规则,将旧数组中的元素转移到新数组中
                    transfer(newTable);
                    table = newTable;
                    //更新临界值
                    threshold = (int)(newCapacity * loadFactor);
                }
    
              //旧数组中元素往新数组中迁移
                void transfer(Entry[] newTable) {
                    //旧数组
                    Entry[] src = table;
                    //新数组长度
                    int newCapacity = newTable.length;
                    //遍历旧数组
                    for (int j = 0; j < src.length; j++) {
                        Entry<K,V> e = src[j];
                        if (e != null) {
                            src[j] = null;
                            do {
                                Entry<K,V> next = e.next;
                                int i = indexFor(e.hash, newCapacity);
                                e.next = newTable[i];
                                newTable[i] = e;
                                e = next;
                            } while (e != null);
                        }
                    }
                }

    5.HashMap的性能参数:

    HashMap 包含如下几个构造器:

    1.    HashMap():构建一个初始容量为 16,负载因子为 0.75 的 HashMap。
    2.    HashMap(int initialCapacity):构建一个初始容量为 initialCapacity,负载因子为 0.75 的 HashMap。
    3.    HashMap(int initialCapacity, float loadFactor):以指定初始容量、指定的负载因子创建一个 HashMap。
    4.    HashMap的基础构造器HashMap(int initialCapacity, float loadFactor)带有两个参数,它们是初始容量initialCapacity和加载因子loadFactor。
    5.    initialCapacity:HashMap的最大容量,即为底层数组的长度。
    6.    loadFactor:负载因子loadFactor定义为:散列表的实际元素数目(n)/ 散列表的容量(m)。

      负载因子衡量的是一个散列表的空间的使用程度,负载因子越大表示散列表的装填程度越高,反之愈小。对于使用链表法的散列表来说,查找一个元素的平均时间是O(1+a),因此如果负载因子越大,对空间的利用更充分,然而后果是查找效率的降低;如果负载因子太小,那么散列表的数据将过于稀疏,对空间造成严重浪费。

      HashMap的实现中,通过threshold字段来判断HashMap的最大容量:

    threshold = (int)(capacity * loadFactor);  

      结合负载因子的定义公式可知,threshold就是在此loadFactor和capacity对应下允许的最大元素数目,超过这个数目就重新resize,以降低实际的负载因子(也就是说虽然数组长度是capacity,但其扩容的临界值确是threshold)。默认的的负载因子0.75是对空间和时间效率的一个平衡选择。当容量超出此最大容量时, resize后的HashMap容量是容量的两倍:

    if (size++ >= threshold)   
        resize(2 * table.length); 

    6.Fail-Fast机制:

      我们知道java.util.HashMap不是线程安全的,因此如果在使用迭代器的过程中有其他线程修改了map,那么将抛出ConcurrentModificationException,这就是所谓fail-fast策略。(这个在core java这本书中也有提到。)

      这一策略在源码中的实现是通过modCount域,modCount顾名思义就是修改次数,对HashMap内容的修改都将增加这个值,那么在迭代器初始化过程中会将这个值赋给迭代器的expectedModCount。

    HashIterator() {
        expectedModCount = modCount;
        if (size > 0) { // advance to first entry
        Entry[] t = table;
        while (index < t.length && (next = t[index++]) == null)
            ;
        }
    }

      在迭代过程中,判断modCount跟expectedModCount是否相等,如果不相等就表示已经有其他线程修改了Map:

      注意到modCount声明为volatile,保证线程之间修改的可见性。(volatile之所以线程安全是因为被volatile修饰的变量不保存缓存,直接在内存中修改,因此能够保证线程之间修改的可见性)。

    final Entry<K,V> nextEntry() {   
        if (modCount != expectedModCount)   
            throw new ConcurrentModificationException();

    在HashMap的API中指出:

      由所有HashMap类的“collection 视图方法”所返回的迭代器都是快速失败的:在迭代器创建之后,如果从结构上对映射进行修改,除非通过迭代器本身的 remove 方法,其他任何时间任何方式的修改,迭代器都将抛出ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不保证在将来不确定的时间发生任意不确定行为的风险。

      注意,迭代器的快速失败行为不能得到保证,一般来说,存在非同步的并发修改时,不可能作出任何坚决的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的做法是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。

    7.其他问题

    1.为什么String, Interger这样的wrapper类适合作为键?

    String, Interger这样的wrapper类作为HashMap的键是再适合不过了,而且String最为常用。因为String是不可变的,比如你放进去的key是

    "str1",那就永远是"str1",不会又变成"str2",但如果放进去的是一个对象,那万一这个对象里的属性变了,那不就改变key了。

    2.我们可以使用自定义的对象作为键吗?

    是第一问的扩展,如果这个自定义对象是不可变的,那么它已经满足了作为键的条件,因为当它创建之后就已经不能改变了,那可以作为key,前提就是它会不会变。

    展开全文
  • 在一篇博客里看到的,这一块讲解的很好,所以截图啦

    在一篇博客里看到的,这一块讲解的很好,所以截图啦
    hashtable与HashMap的对比

    展开全文
  • 问题:mybatis 返回HashMapnull,但是执行语句有数据。 问题跟踪:...

    问题:mybatis 返回HashMap为null,但是执行语句有数据。

    问题跟踪:http://mybatis-user.963551.n3.nabble.com/SQLSession-selectOne-for-resultType-quot-map-quot-and-resultType-quot-hashmap-quot-returns-null-td2236152.html

    问题产生原因:

    因为我mybatis设置

    auto-mapping-behavior = none,对象的创建都只用ResultMap创建,不需要automap行为,使用automap反而会影响性能,所以我没有使用partial或者full。auto-mapping-behavior = none会让配置resultType="HashMap"无效,永远放回null,不能得到HashMap对象。

     

    解决:

    设置一个全局的

    <resultMap id="HashMap" type="java.util.HashMap" autoMapping="true">
    </resultMap>

    当要返回HashMap的时候,查询数据返回Map设置HashMap,如:

    <select id="statistics" parameterType="app.pridecommunity.masonsoft.query.TicketQuery"
                resultMap="HashMap" >
            select
              count(ticket_id) as total
            from ofTicket
            
        </select>

    唯一的区别就是resultType改为resultMap。

    展开全文
  • HashMap put返回Null的问题

    千次阅读 2018-05-22 11:17:19
    今早一过来就遇到一个bug,result.setData数据没写入成功,具体代码如下:result.setData(new ...debug后发现data中的数据是null;查hashMap源码:public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR;...

    今早一过来就遇到一个bug,result.setData数据没写入成功,具体代码如下:

    result.setData(new HashMap<>().put("stepType",stepType));

    debug后发现data中的数据是null;

    查hashMap源码:
    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }

    创建对象没啥问题,下一步查put的源码:
    /**
     * Associates the specified value with the specified key in this map.
     * If the map previously contained a mapping for the key, the old
     * value is replaced.
     *
     * @param key key with which the specified value is to be associated
     * @param value value to be associated with the specified key
     * @return the previous value associated with <tt>key</tt>, or
     *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
     *         (A <tt>null</tt> return can also indicate that the map
     *         previously associated <tt>null</tt> with <tt>key</tt>.)
     */
    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
    看作者的注释: the previous value associated with key, or null if there was no mapping for key;
    返回的是插入的key对应的上一个value,如果没有这个key的话,返回的就是null。
    so,我们的put方法,不是返回的HashMap对象


    看到这能知道为什么我们new HashMap<>().put("stepType",stepType)返回的是null了。再看一下put的具体实现:
    /**
     * Implements Map.put and related methods
     *
     * @param hash hash for key
     * @param key the key
     * @param value the value to put
     * @param onlyIfAbsent if true, don't change existing value
     * @param evict if false, the table is in creation mode.
     * @return previous value, or null if none
     */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
    这是JDk1.8的源码,put方式已经改成的红黑树实现,具体实现方法可自行解析。我们这边关注return oldValue;可以看到返回的是前值。

    所以具体到刚才的业务代码,我们可以做如下修改:
    result.setData(new HashMap<>().put("stepType",stepType));
    使用匿名内部类:
    result.setData(new HashMap(){
        {
            put("stepType",stepType);
        }
    });

    搞定!

    还是基本功的问题啊,虽然扒下源码,很快就能改完,但这种问题以后还是尽量避免出现吧~

    展开全文
  • HashMap在put的时候会调用hash()方法来计算key的hashcode值,可以从hash算法中看出当key==null时返回的值为0。因此key为null时,hash算法返回值为0,不会调用key的hashcode方法。 HashTable源码 上面可以看出当...
  • 1.HashMap支持null键和null值JSE6.0描述:基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)...
  • 10分钟拿下 HashMap

    万次阅读 多人点赞 2018-10-18 11:18:04
    1、什么是 HashMap?什么时候选择HashMap? 2、HashMap 数据结构及其工作原理? 2.1 数据结构 2.2 工作原理 3、HashMap和HashTable 的异同? 4、如何优化 HashMap? 1、什么是 HashMap?什么时候选择HashMap?...
  • HashMap不能保证元素的顺序,HashMap能够将键设为null,也可以将值设为null。 与之对应的是Hashtable,(注意大小写:不是HashTable),Hashtable不能将键和值设为null,否则运行时会报空指针异常错误; HashMap线程不...
  • importjava.util.HashMap; importjava.util.Map; importjava.util.TreeMap; publicclassTestMain { publicstaticvoidmain(String[] args) { ...// HashMap可以的键值可以是null, "". M...
  • 先说结论:hashmap允许多个null值和一个null键,hashtable不允许有任何null值和null键 基于jdk1.8源码分析: 我们都知道Map作为集合中比较重要的键值对集合,key不能重复,所以可以初步判断hashmap和hashtable都不...
  • HashMap判断键是否为null

    千次阅读 2018-01-12 10:51:00
    当get()方法返回null值时,即可以表示HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键,而应该用containsKey()方法来判断。 转载于:http...
  • HashMap里面key为null存放到哪?

    千次阅读 2020-08-06 12:47:49
    我们知道HashMap集合是允许存放null值的 hashMap是根据key的hashCode来寻找存放位置的,那当key为null时, 怎么存储呢? 在put方法里头,其实第一行就处理了key=null的情况。 // HashMap的put方法 public V put(K ...
  • 写这么多只是为了记录自己此次解决问题的过程和个中原理,可能会比较啰嗦,若不想看,拉到文章最下面直接看解决方案。在进行ibatis升级至mybatis中,遇到... list的返回结果为[null],size=1。这个肯定是有问题的,...
  • HashMap中插入null key的过程分析

    万次阅读 2016-03-08 12:31:47
    HashMap中添加key==null的Entry时会调用putForNullKey方法 下面是HashMap的put方法: public V put(K key, V value) { if (table == EMPTY_TABLE) { inflateTable(threshold); } if (key ==
  • hashMap判断是否为空null失败

    千次阅读 2020-06-02 21:32:40
    原因:new出来的hashMap存在一个对象引用地址,所以就不能为null,空集合和null是有区别的 解决问题关键代码:在循环外面赋值为null,进循环后再创建对象,这样在数据库如果没有取到值就不会进入循环 public Map<...
  • HashMap 对于key是null值的存储

    千次阅读 2019-03-04 20:44:32
    https://www.jianshu.com/p/dfffb4b06d6e ... HashMap 允许插入键为 null 的键值对。但是因为无法调用 null 的 hashCode() 方法,也就无法确定该键值对的桶下标,只能通过强制指定一个桶下标来存放。HashMap 使...
  • 本文转载自:... ...在HashMap中添加key==null的Entry时会调用putForNullKey方法 下面是HashMap的put方法: [java] view plain copy p
  • hashMap

    千次阅读 2018-08-02 20:34:01
    HashMap HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。HashMap的底层结构是一个数组,数组中的每一项是一条链表 HashMap的实例有两个参数影响其性能:“初始... HashMap可以存null键和null值...
  • list可以,hashmap的key 和vaule都可以放null ,hashset可以 对于Map里面的键和值是否可以为空的问题,答案是:不一定。对于HashMap来说,可以存放null键和null值,而HashTable则不可以。 map.put(0, "0")...
  • HashMap 中插入null key 的过程

    千次阅读 2017-08-30 20:20:28
    添加key==null的Entry时,调用putForNullKey方法 正常的是put方法: 当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象。 当两个不同的键对象的...
  • Hashmap

    2018-03-01 12:09:16
    Hashmap是java面试中... HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。 2. HashMap是非synchronized,...
  • 8.null键放在HashMap的哪里 HashMapnull作为key时,总是存储在table数组的第一个节点上 它是根据key的hashCode来寻找存放位置的,在put方法里头,其实第一行就处理了key=null的情况。 // HashMap的put方法 ...
  • HashMap中key为null时存到哪里去了

    千次阅读 2019-03-04 11:20:34
    hashMap是根据key的hashCode来寻找存放位置的,那当key为null时, 怎么存储呢? 在put方法里头,其实第一行就处理了key=null的情况。 if (key == null) return putForNullKey(value); //那就看看这个...
  • hashMap是根据key的hashCode来寻找存放位置的,那当key为null时, 怎么存储呢? 答案: 当key为null时,value存在了HashMap底层table数组的table[0]位置,之后具体的存储方式和其他索引位置一致
  • 我们知道,HashMap允许有 null 的 key 和 null 的 value,但是当加入多个null key时,后面的数据会覆盖前面的数据。实验如下: 源码 public static void main(String[] args) { HashMap<Integer,String> ...
  • HashMap可以存储一个Key为null,多个value为null的元素,但是Hashtable却不可以存储 HashMap.class: // 此处计算key的hash值时,会判断是否为null,如果是,则返回0,即key为null的键值对 // 的hash为0。因此...
  • Hashtable 是线程安全的,ConcurrentHashMap 和 Hashtable 是不允许 null 作为键和值的,HashMap允许null做为键值
  • HashMap

    千次阅读 多人点赞 2018-09-25 12:09:44
    title: HashMap date: 2018-09-24 11:33:31 tags:HashMap 前一部分是拿的大佬的在这声明,后部分是自己看课程总结的 在这里贴上大佬的地址:https://www.jianshu.com/p/52066d6b7717 1.HashMap的实现原理 1,...
  • jdk8中HashMap如何处理key为null的情况

    千次阅读 2019-07-30 15:26:03
    首先,我们来看HashMap的put方法 传入的参数有五个,我们在此只看第一个,hash(key),查看代码 得知,当key为null时,返回值为0; 进入putVal(...),下方为完整代码,可跳过 final V putVal(int ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 466,837
精华内容 186,734
关键字:

hashmap的null