Map双列集合

  • Map接口:双列集合,用来存储一对(key - value)一对的数据  --->高中函数:y=f(x)
    • HashMap:作为Map的主要实现类:线程不安全的,效率高;可以存储null的key和value
      • LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历。
      • 原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个添加的元素,对于频繁的遍历操作,此类执行效率高于HashMap
    • TreeMap:保证按照添加的key-value进行排序,实现排序遍历,此时考虑key的自然排序或定制排序,底层使用红黑树
    • Hashtable:作为古老的实现类:线程安全的,效率低:不能存储null的key和value
      • Properties:通常用来处理配置文件,key和value都是String类型

Map的底层:数组+链表(jdk 7及之前)

      数组+链表+红黑树(jdk 8)

 二、Map结构的理解:

 Map中的kye:无序的、不可重复的,使用Set存储所有的key  --> key所在的类要重写equals()和hashCode()(以HashMap为例)

Map中的value:无序的、可重复的,使用Collection存储所有的value  -->value所在的类需要重写equals()

一个键值对:key-value构成了一个Entry对象。

Map中的entry:无序的、不可重复的,使用Set存储所有的entry

三、HashMap的底层实现原理?以jdk 7为例说明:

HashMap map = new HashMap():

在实例化以后,底层创建了长度是16的一维数组Entry[] table

可能已经执 行官多次put

map.put(key,value):

首先,调用key所在类的hashCode()计算key哈希值;

根据传入的key,计算hash值,调用对象.hashCode()获取. 进行高16位和低16位进行扰动计算.

判断成员变量table是否为空,如果为空,说明该还没有初始化.

调用resize进行初始化,初始化默认长度为16,拓容阈值为16*0.75. 如果构造方法指定了数组的长度,按照指定的来(如果输入的长度不是2的幂次方,默认找到离传入值最近的2的幂次方的数值作为数组的长度)

使用hash & (数组长度-1) 等于求模取余 ,计算当前元素需要存放的索引位置

当前位置的元素为空

创建Node对象,将key,value,hash值存储到对象中

当前位置的元素不为空

判断当前传入的元素和数组位置上的元素是否相同元素(1.判断hash值是否相等2.判断内存地址或者equals相等),如果是相同对象,把旧的值取出,将新的值覆盖,返回旧的值

判断当前节点是否TreeNode对象,如果是,说明当前节点已经是红黑树,元素往红黑树中添加元素

遍历链表,判断链表上的元素是否和当前传入的元素时相同元素,如果是相同对象,把旧的值取出,将新的值覆盖,返回旧的值,如果不是,已经达到链表尾部,在链表尾部插入元素,返回null.

如果链表大于8,但数组长度小于64,仅仅进行拓容,不会转成红黑树

如果链表大于8,但数组长度大于64,转成红黑树

将链表变成双向链表,然后再转成红黑树.

    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

如果此位置上的数据为空,此时的key1--value1添加成功(实际上是Entry) ---情况1

如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(多个数据以链表形式存在)),比较key1和已经存在的一个或者多个数据的哈希值:

  如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功  --情况2

  如果key1的哈希值与已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)

     如果equals()返回false:此时key1-value1添加成功。  --情况3

     如果equals()返回true:使用value1替换value2

  补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储。

在不断的添加过程中,会涉及到扩容问题,默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来。

jdk 8相较于jdk 7在底层实现方面的不同:

1、new HashMap():底层没有创建一个长度为16的数组

2、jdk 8底层的数组是Node[],而非Entry[]

3、首次调用put()方法时,底层创建长度为16的数组

4、jdk 7底层结构只有:数组+链表,jkd 8中底层结构:数组+链表+红黑树

  当数组的某一个索引位置上的元素以链表的形式存在的数据个数>8且当前数组的长度>64时,此时索引位置上的所有数据改为使用红黑树存储。

四、LinkedHashMap的底层原理

五、map的常用方法

添加、删除、修改操作:

object put(object key, object value): 将指定key-value 添加到(或修改)当前map对象中

void putALl(Map m): 将m中的所有key-value对存放到当前map中

object remove(object key):移除指定key的key-value对,并返回value

void clear(): 清空当map中的所有数据元素

    static void test(){
        Map map = new HashMap();
        //添加
        map.put("a",56);
        map.put(1,55);
        map.put("b",54);
        //修改
        map.put("a",88);

        System.out.println(map);

        Map map1 = new HashMap();
        map1.put("c",55);
        map1.put("d",8);
        map1.put("a",22);
        //将m中的所有key-value对存放到当前map中
        map1.putAll(map);
        System.out.println(map1);
        //移除指定key的key-value对,并返回value
        Object value = map1.remove("c");
        System.out.println(value);
        System.out.println(map1);
        //清空当map中的所有数据元素
        map1.clear();
        System.out.println(map1);
    }

查询的操作:

object get(object key):获取指定key对应的value

boolean containsKey(object key):是否包含指定的key

boolean containsValue(0bject value):是否包含指定的value

int size(): 返回map中key-value对的个数

boolean isEmpty(): 判断当前map是否为空

boolean equals(object obj): 判断当前map和参数对象obj是否相等

static void test1(){
        Map map = new HashMap();
        //添加
        map.put("a",56);
        map.put(1,55);
        map.put("b",54);
        //获取指定key对应的value
        System.out.println(map.get("a"));
        //是否包含指定的key
        System.out.println(map.containsKey("c"));
        System.out.println(map.containsKey("a"));
        //是否包含指定的value
        System.out.println(map.containsKey(80));
        System.out.println(map.containsValue(55));
        //返回map中key-value对的个数
        System.out.println(map.size());
        //判断当时map是否为空
        System.out.println(map.isEmpty());

    }

元视图操作的方法:

Set keySet():返回所有key构成的Set集合

Collection values(): 返回所有value构成的Collection集合

Set entrySet(): 返回所有key-value对构成的Set集合

    static void test2(){
        Map map = new HashMap();
        //添加
        map.put("a",56);
        map.put(1,55);
        map.put("b",54);
        //遍历所有的key集合
        Set set = map.keySet();
        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
        //遍历所有的value集合
        Collection list = map.values();
        iterator = list.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
        //遍历所有key-value对
        set = map.entrySet();
        iterator = set.iterator();
        while (iterator.hasNext()){
            Map.Entry entry = (Map.Entry) iterator.next();
            System.out.println(entry);
            System.out.println(entry.getKey());
            System.out.println(entry.getValue());
        }
    }

总结:常用方法:

添加: put(Object key, Object value)

删除: remove(Object key)

修改: put(Object key, Object value)

查询: get(Object key)长度: size()

遍历: keySet() / values() / entrySet()

六、TreeMap的排序

    static void test(){
        TreeMap map = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                if (o1 instanceof User && o2 instanceof User){
                    User u1 = (User) o1;
                    User u2 = (User) o2;
                    //根据年龄从小到大排序,如果年龄相同按照姓名排序
                    int compare = Integer.compare(u1.getAge(),u2.getAge());
                    if (compare!=0){
                        return compare;
                    }
                    return u1.getName().compareTo(u2.getName());
                }
                throw new RuntimeException("类型不匹配");
            }
        });
        User user1 = new User("a张三",23);
        User user2 = new User("b李四",23);
        User user3 = new User("c王五",28);
        User user4 = new User("d赵六",19);
        User user5 = new User("a张三",18);

        map.put(user1,80);
        map.put(user2,40);
        map.put(user3,88);
        map.put(user4,99);
        map.put(user5,89);

        Set set = map.entrySet();
        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            Map.Entry entry = (Map.Entry) iterator.next();
            System.out.println(entry.getKey()+"——————"+entry.getValue());
        }
    }

 六、Properties

Properties:常用来处理配置文件。key和value都是String类型

 

posted @ 2022-07-28 16:08  羽十六  阅读(46)  评论(0)    收藏  举报