Map相关知识记录(1.0)

Map

映射(Map)是一种十分常用和基础的数据结构,用来存放键/值对。如果提供了键,就能查找到值。
在这里插入图片描述
1.HashMap:它根据键的hashCode值存储数据,大多数情况下可以直接定位到它的值,因而具有很快的访问速度。
HashMap最多只允许一条记录的键为null,允许多条记录的值为null。非线程安全。
如果需要满足线程安全,可以用 Collections的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap

2.Hashtable:Hashtable是遗留类,很多映射的常用功能与HashMap类似,不同的是它承自Dictionary类。线程安全。并发性不如ConcurrentHashMap,因为ConcurrentHashMap引入了分段锁。

3.LinkedHashMap:LinkedHashMap是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的,也可以在构造时带参数,按照访问次序排序。

4.TreeMap:TreeMap实现SortedMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。
在使用TreeMap时,key必须实现Comparable接口或者在构造TreeMap传入自定义的Comparator,否则会在运行时抛出java.lang.ClassCastException类型的异常。

Map常用接口

java.util.Map<K,V> 1.2
• V get(Object key)
获取与键对应的值;返回与键对应的对象, 如果在映射中没有这个对象则返回 null。键可以为 null。
• default V getOrDefault(Object key, V defaultValue)
获得与键关联的值;返回与键关联的对象, 或者如果未在映射中找到这个键, 则返回defaultValue。
V put(K key, V value)
将键与对应的值关系插入到映射中。如果这个键已经存在, 新的对象将取代与这个键对应的旧对象。这个方法将返回键对应的旧值。如果这个键以前没有出现过则返回null。键可以为 null, 但值不能为 null。
• void putAll(Map<? extends K , ? extends V> entries)
将给定映射中的所有条目添加到这个映射中。
• boolean containsKey(Object key)
如果在映射中已经有这个键, 返回 true。
boolean containsValue(Object value)
如果映射中已经有这个值, 返回 true。
•default void forEach(BiConsumer<? super K ,? super V> action)8
对这个映射中的所有键 / 值对应用这个动作。
public V remove(Object key):把指定的键所对应的键值对元素在Map集合中删除返回被删除元素的值.
在这里插入图片描述

 public static void mapDemo() {
        //创建Map对象
        Map<String, String> map = new HashMap<String, String>();
        //给map添加元素
        map.put("星期一", "Monday");
        map.put("星期二", "Tuesday");
        map.put("星期日", "Sunday");
        System.out.println(map);

        System.out.println(map.get("星期一"));
        System.out.println(map.get("Monday"));
        System.out.println(map.containsValue("Monday"));
        System.out.println(map.remove("星期一"));
        System.out.println(map);
    }

结果
注意:Map集合不能直接使用迭代器或者foreach进行遍历。但是转成Set之后就可以使用了。所以在获取map里的所有键或者所有值的时候,都要通过方法先转换成Set,再遍历,比如

	map.keySet();
    map.entrySet();

HashMap

HashMap是数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的。
在这里插入图片描述
这里需要讲明白两个问题:数据底层具体存储的是什么?这样的存储方式有什么优点呢?

1.HashMap类中有一个非常重要的字段,就是 Node[] table,即哈希桶数组

static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;    //用来定位数组索引位置
        final K key;
        V value;
        Node<K,V> next;   //链表的下一个node

        Node(int hash, K key, V value, Node<K,V> next) { ... }
        public final K getKey(){ ... }
        public final V getValue() { ... }
        public final String toString() { ... }
        public final int hashCode() { ... }
        public final V setValue(V newValue) { ... }
        public final boolean equals(Object o) { ... }
}

Node是HashMap的一个内部类,实现了Map.Entry接口,本质是就是一个映射(键值对)。上图中的每个黑色圆点就是一个Node对象。

2.HashMap就是使用哈希表来存储的。Java中HashMap采用了拉链法解决冲突。
例如程序执行下面代码:

map.put("美团","小美");

系统将调用"美团"这个key的hashCode()方法得到其hashCode 值(该方法适用于每个Java对象),然后再通过Hash算法的后两步运算(高位运算和取模运算,下文有介绍)来定位该键值对的存储位置。

哈希桶数组需要在空间成本和时间成本之间权衡。那么通过什么方式来控制map使得Hash碰撞的概率又小,哈希桶数组(Node[] table)占用空间又少呢?答案就是好的Hash算法和扩容机制。(待补充)

LinkedHashMap

HashMap有一个顺序的问题,就是在对HashMap进行迭代访问时,添加的顺序和访问的顺序可能就不一样的,这个时候我们可以选择LinkedHashMap,LinkedHashMap继承了HashMap,所以拥有和HashMap一样的功能;而且在此基础上有增加了一个双向链表来实现元素迭代的顺序,但是肯定会增加时间和空间的消耗,LinkedHashMap和HashMap一样,也是非线程安全的

public static void mapDemo() {
        HashMap<String, Object> hashMap = new HashMap<String, Object>();
        hashMap.put("name","chenweijie");
        hashMap.put("age", 22);
        hashMap.put("addr", "北京");
        hashMap.put(null, null);
        hashMap.put("abc", null);
        hashMap.put(null, "bcd");

        LinkedHashMap<String, Object> hashMap1 = new LinkedHashMap<String, Object>();
        hashMap1.put("name","chenweijie");
        hashMap1.put("age", 22);
        hashMap1.put("addr", "北京");
        hashMap1.put(null, null);
        hashMap1.put("abc", null);
        hashMap1.put(null, "bcd");

        for(Map.Entry<String, Object> entry : hashMap.entrySet()) {
            System.out.println(entry.getKey() + "=" +entry.getValue());
        }
        System.out.println("--------------------------------------------");
        for(Map.Entry<String, Object> entry : hashMap1.entrySet()) {
            System.out.println(entry.getKey() + "=" +entry.getValue());
        }
    }

结果:
在这里插入图片描述
可以看到一个无序输出,一个有序输出。
根据输出我们可以得出以下几个结论:
LinkedHashMap的输入顺序和输出顺序是一致的。
LinkedHashMap允许Key和Value都可以null(可是hashmap好像也可以啊?)
LinkedHashMap中添加元素时,如果Key重复,则后添加的会覆盖前面已经存在的值

LinkedHashMap的具体实现(待补充)

 /**
     * HashMap.Node subclass for normal LinkedHashMap entries.
     */
    static class Entry<K,V> extends HashMap.Node<K,V> {
        Entry<K,V> before, after;
        Entry(int hash, K key, V value, Node<K,V> next) {
            super(hash, key, value, next);
        }
    }

Entry就是LinkedHashMap基本数据结构,Entry是LinkedHashMap定义的一个内部类,继承了HaspMap.Entry,在此基础上添加了新添加了两个属性。
before、after是用于维护链表中Entry的前一个元素和后一个元素。

在HashMap的构造函数中最后有一个init的方法,但是此方法在HashMap中没有实现,LinkedHashMap中重写了该方法,用来初始化化链表。

总结:从以上可以判断LinkedHashMap的实现就是 HashMap+LinkedList 的实现方式,用HashMap维护数据结构,用LinkList的方式维护数据插入顺序。

参考:
java核心技术卷I
https://blog.csdn.net/qq_29373285/article/details/81487594
https://www.cnblogs.com/xawei/p/6747660.html
https://zhuanlan.zhihu.com/p/38615394
https://www.cnblogs.com/benwu/articles/7751640.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值