HashMap的知识回顾

2022-06-14 16:45:34 浏览数 (1)

项目中Map的使用非常频繁,对于map的知识点的学习回顾是非常有必要的

知识点:

  • HashMap 中的方法在默认情况下是非同步的,这一点和HashTabel对比就可以看出,HashTabel的方法是有synchronized关键词修饰的
  • HashMap 中,null 可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为 null
代码语言:javascript复制
    @Test
    public void test(){
        Map<String, Object> map = new HashMap<>();
        map.put(null, null);
        System.out.println(map);
    }

输出结果:{null=null}

往HashMap中存放键值对时,如果key相同,则最新加入的值会覆盖掉原有的值

代码语言:javascript复制
    @Test
    public void test(){
        Map<String, Object> map = new HashMap<>();
        map.put(null, null);
        map.put(null, "123");
        System.out.println(map);
    }
输出的结果为:{null=123}

ctrl fn F12查看文件结构

HashMap 实现 Iterator,支持 fail-fast

fail-fast是集合世界中错误检测机制,通常出现在集合元素的遍历过程中, java.util包下所有的类都是fail-fast,而concurrent包中的集合都是fail-safe

HashMap如何遍历

方法一: 测试demo

代码语言:javascript复制
    @Test
    public void test(){
        Map<String, Object> map = new HashMap<>();
        map.put(null, null);
        map.put("name", "123");
        map.put("address", "中国");
        map.put("age", "30");
    for(Map.Entry<String, Object> entry : map.entrySet()){
        System.out.println("key = "   entry.getKey()   ", value = "   entry.getValue());
    }
    }

输出结果: key = null, value = null key = address, value = 中国 key = name, value = 123 key = age, value = 30

For-Each循环是Java5新引入的,所以只能在Java5以上的版本中使用。如果你遍历的map是null的话,For-Each循环会抛出NullPointerException异常,所以在遍历之前你应该判断是否为空引用

方法二:

  • 仅获取key
代码语言:javascript复制
@Test
    public void getKey(){
        Map<String, Object> map = new HashMap<>();
        map.put(null, null);
        map.put("name", "123");
        map.put("address", "中国");
        map.put("age", "30");
        for (String key : map.keySet()) {
            System.out.println("Key = "   key);
        }
    }

// lambda表达式来写的话就一行代码
 map.forEach((k,v) -> System.out.println(k));

结果: Key = null Key = address Key = name Key = age

  • 仅获取value
代码语言:javascript复制
    @Test
    public void getvalue(){
        Map<String, Object> map = new HashMap<>();
        map.put(null, null);
        map.put("name", "123");
        map.put("address", "中国");
        map.put("age", "30");
        for (Object value : map.values()) {
            System.out.println("Value = "   value);
        }

    }

 map.forEach((k,v) -> System.out.println(v));

输出结果: Value = null Value = 中国 Value = 123 Value = 30

特点

HashMap中的方法都是异步处理,属于非线程安全

问题

1 为什么HashMap初始容量是2<<4 ?或HashMap的初始容量是多少

代码语言:javascript复制
    /**
     * The default initial capacity - MUST be a power of two.
     */
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

这个上边注释的意思是,默认初始容量-必须是2的幂

作用: 1.提醒你这个容量就是2的幂,扩容方式也是2的幂。 2.二的幂是使得Key Hash算法后的值尽可能均匀的分布在Map对应的数组位置的合理值

size记录hashMap中KV对的个数

代码语言:javascript复制
   /**
     * The number of key-value mappings contained in this map.
     */
    transient int size;

阈值:

代码语言:javascript复制
  // TREEIFY_THRESHOLD表示树形阈值
  static final int TREEIFY_THRESHOLD = 8;

在HashMap中存储数据超过8的时候(阈值) 就会由链表转为红黑树(类似于二分查找)

扩容机制

代码语言:javascript复制
    /**
     * The load factor used when none specified in constructor.
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

HashMap中对于数据的保存个数的扩充是按照倍数来进行的,如果达到了16*0.75个数的时候回进行第一次扩充,而后扩充一倍变为32

hash(Object key)

代码语言:javascript复制
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

HashMap的原理:

JDK1.8之前

JDK1.8 之前 HashMap 底层是 数组和链表 结合在一起使用也就是 链表散列。HashMap 通过 key 的 hashCode 经过扰动函数处理过后得到 hash 值,然后通过 -判断当前元素存放的位置(这里的 n 指的时数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突

JDK1.8

JDK1.8之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。

HashMap中哈希冲突严重时会影响HashMap性能,该如何做

首先要知道什么是哈希冲突,在整个Hash存储过程中,必须要明确两个实际问题, hashcode()和equal()

Map.Entry和Node<K,V>

参考文章:https://www.hollischuang.com/archives/2091

Java8中遍历Map的常用四种方式

https://mp.weixin.qq.com/s?__biz=MzUyMzcyNjY4Mw==&mid=2247484703&idx=2&sn=1e5aa63f5806f8ebac6f066368b64d09&chksm=fa397f2bcd4ef63df718055f61fc506e0e47a847dbe6b21021815039267522209402abb02c79&scene=21#wechat_redirect

HashTable

HashTable(线程安全) Hashtable 是遗留类,很多映射的常用功能与 HashMap 类似,不同的是它承自 Dictionary 类,并且是线程安全的,任一时间只有一个线程能写 Hashtable,并发性不如 ConcurrentHashMap,因为 ConcurrentHashMap 引入了分段锁。Hashtable 不建议在新代码中使用,不需要线程安全的场合可以用 HashMap 替换,需要线程安全的场合可以用 ConcurrentHashMap 替换

待时间充足再更新.....

0 人点赞