映射(Map)相关

  • Map
  • Map常用接口
  • HashMap
  • LinkedHashMap
  • LinkedHashMap的具体实现(待补充)


Map

映射(Map)是一种十分常用和基础的数据结构,用来存放键/值对。如果提供了键,就能查找到值。

android一个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集合中删除返回被删除元素的值.

android一个Map 可以存放多大数据 map可以存放多少条数据_bc_02

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);
    }

android一个Map 可以存放多大数据 map可以存放多少条数据_bc_03


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

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

HashMap

HashMap是数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的。

android一个Map 可以存放多大数据 map可以存放多少条数据_System_04


这里需要讲明白两个问题:数据底层具体存储的是什么?这样的存储方式有什么优点呢?

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());
        }
    }

结果:

android一个Map 可以存放多大数据 map可以存放多少条数据_bc_05


可以看到一个无序输出,一个有序输出。

根据输出我们可以得出以下几个结论:

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的方式维护数据插入顺序。