• Java集合框架详解(四)——Map接口、HashMap类、LinkedHashMap类


    一、Map接口

    1. Map接口的特点:

    (1)映射键值对的形式(key和value);
    (2)Map集合中,key是不能重复的,value是可以重复的;
    (3)Map集合是散列存放数据的,所以存储顺序与遍历顺序是无序的,即遍历顺序可能与存储顺序不一致。

    1. Map集合的获取方法:
    方法名称说明
    V get(Object key)根据key获取值
    Set keySet()获取所有健的集合
    Collection values()获取所有值的集合
    Set entrySet()获取所有健值对象的集合
    default V getOrDefult(Object key, V defaultValue)如果存在相应的key则返回其对应的value,否则返回给定的默认值defaultValue
    eg:
    public static void main(String[] args) {
            Map<String, String> hashMap = new HashMap<>();
            hashMap.put("qize01", "小丽");
            hashMap.put("qize02", "天天");
            hashMap.put("qize03", "阿奇");
            hashMap.put("qize04", "毛毛");
            //1、根据键获取值
            System.out.println(hashMap.get("qize01"));
            System.out.println(hashMap.get("ss"));
            //2、获取所有健的集合
            Set<String> stringSet = hashMap.keySet();
            for (String key:stringSet){
                System.out.println(key);
            }
            //3、获取所有value的集合
            Collection<String> values = hashMap.values();
            for (String value:values){
                System.out.println(value);
            }
            //4、获取所有键值对象的集合
            Set<Map.Entry<String, String>> entrySet = hashMap.entrySet();
            for (Map.Entry<String, String> entry:entrySet){
                System.out.println(entry);
            }
            //5、如果存在相应的key,则返回其对应的value,否则返回给定的默认值defaultValue
            String qize01 = hashMap.getOrDefault("qize0001","默认值");
            System.out.println(qize01);
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    二、HashMap类

    1、HashMap类特点:
    (1)HashMap类实现Map接口;
    (2)HashMap类是散列存放数据的,所以存储顺序与遍历顺序是无序的,即遍历顺序可能与存储顺序不一致。
    (3)HashMap:底层是基于数组+链表结构来实现的(JDK1.7);基于数组+链表+红黑树实现的(JDK1.8);是线程不安全的。

    eg:

    Map<String,String> hashMap = new HashMap<>();
    hashMap.put("qize001","天天");
    hashMap.put("qize002","小丽");
    hashMap.put("qize003","莱德");
    hashMap.put("qize004","古薇");
    System.out.println(hashMap);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2、HashMap类常用方法

    方法名称解释
    V put(K key, V value)添加元素
    V remove(K key, V value)根据健值删除对应的值
    void clear()清除所有健值元素
    boolean containsKey(Object key)判断集合中是否包含指定的健
    boolean containsValue(Object value)判断集合中是否包含指定的值
    boolean isEnpty()判断集合是否为空

    eg:

    public static void main(String[] args) {
    	Map<String, String> hashMap = new HashMap<>();
        //put方法
        hashMap.put("qize001","天天");
        hashMap.put("qize002","阿奇");
        hashMap.put("qize003","小丽");
        System.out.println(hashMap);
        System.out.println("------------------");
        //判断集合中是否包含指定的健
        System.out.println(hashMap.containsKey("qize002"));
        //判断集合中是否包含指定的值
        System.out.println(hashMap.containsValue("小丽"));
        //根据健值删除对应的值
        hashMap.remove("qize002");
        System.out.println(hashMap);
        //判断集合是否为空
        System.out.println(hashMap.isEmpty());
        //清除所有健值元素
        hashMap.clear();
        System.out.println(hashMap);
        //判断集合是否为空
        System.out.println(hashMap.isEmpty());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    3、HashMap集合的遍历
    (1)先取到HashMap集合中所有的键,再通过增强for循环调用get方法获取对应的value值:
    eg:

    public static void main(String[] args) {
        HashMap<String,String> hashMap = new HashMap<>();
        hashMap.put("qize01","毛毛");
        hashMap.put("qize02","天天");
        hashMap.put("qize03","阿奇");
        Set<String> keySet = hashMap.keySet();
        for (String key:keySet){
            String value = hashMap.get(key);
            System.out.println(key+"-"+value);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    (2)使用entrySet() 方法获取所有健值对象的集合
    eg:

    public static void main(String[] args) {
        HashMap<String,String> hashMap = new HashMap<>();
        hashMap.put("qize01","毛毛");
        hashMap.put("qize02","天天");
        hashMap.put("qize03","阿奇");
        Set<Map.Entry<String, String>> entrySet = hashMap.entrySet();
        for(Map.Entry<String, String> entry:entrySet){
            System.out.println(entry.getKey()+"-"+entry.getValue());
        }
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    (3)使用entrySet() 方法和 Iterator 获取所有健值对象的集合
    eg:

    public static void main(String[] args) {
        HashMap<String,String> hashMap = new HashMap<>();
        hashMap.put("qize01","毛毛");
        hashMap.put("qize02","天天");
        hashMap.put("qize03","阿奇");
        Set<Map.Entry<String, String>> entrySet = hashMap.entrySet();
        Iterator<Map.Entry<String, String>> iterator = entrySet.iterator();
        while (iterator.hasNext()){
            Map.Entry<String, String> entry = iterator.next();
            System.out.println(entry.getKey()+"-"+entry.getValue());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    4、HashMap集合的底层原理
    (1)HashMap 的 key 是否可以设置为自定义对象?可以。
    (2)HashMap 存储数据时有序还是无序的?是无序的。
    (3)HashMap 是否可以存放null 值?如果可以的话,存放在数组中哪个位置呢?可以存放null值,存放在数组index为0的位置。
    (4)HashMap 集合中键值对是如何封装的?是通过实现Map接口封装的Entry对象来封装的。
    eg:

    public static void main(String[] args) {
        //HashMap 的 key 是否可以设置为自定义对象?可以。
        Student student = new Student("qize01",32);
        HashMap<Student, String> hashMap1 = new HashMap<>();
        hashMap1.put(student, "qize");
        //HashMap 存储数据时有序还是无序的?是无序的。
        HashMap<String, String> hashMap2 = new HashMap<>();
        for (int i=0;i<20; i++){
            hashMap2.put("qize"+i, "i:"+i);
        }
        hashMap2.forEach((k,v) -> {
            System.out.println(k+","+v);
        });
        //HashMap 是否可以存放null 值?如果可以的话,存放在数组中哪个位置呢?
        //可以存放null值,存放在数组index为0的位置
        HashMap<String, String> hashMap3 = new HashMap<>();
        hashMap3.put(null,"毛毛");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    (5)key的hash算法原理
    hashMap集合 1.7版本 是基于数组+链表实现的
    hashMap集合 1.8版本 是基于数组+链表+红黑树实现的
    根据key的hashCode 取余entrys.length,得到的余数就是该key存放在我们数组中对应的index位置
    eg:

    public class QizeHashMap<K, V> {
        private  Entry[] entrys = new Entry[10000];
    
        class Entry<K, V> {
            K k;//key
            V v;//value
    
            int hash;//key的hash值
    
            public Entry(K k, V v) {
                this.k = k;
                this.v = v;
            }
        }
        
        public  void put(K k, V v){
            //根据key的hashCode 取余entrys.length
            //得到的余数就是该key存放在我们数组中对应的index位置
            int index = k.hashCode() % entrys.length;
            entrys[index] = new Entry<>(k, v);
        }
    
        public V get(K k){
            //如果根据key查询的话,hashCode 取余entrys.length
            int index = k.hashCode() % entrys.length;
            return (V) entrys[index].v;
        }
    
        public static void main(String[] args) {
            QizeHashMap<Object, Object> hashMap = new QizeHashMap<>();
            hashMap.put("qize", "毛毛");
            System.out.println(hashMap.get("qize"));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    (6)什么是hash冲突问题?
    hashCode相同但是值不同,例如 key=“a” 和key=97 最终计算的存放value的下标index相同,则key=97的存放在数组的键值对(Entry对象)会覆盖掉key="a"存放在数组中的键值对。
    eg:

    //在(5)key的hash算法原理的例子的基础上修改main方法,如下
    public static void main(String[] args) {
        QizeHashMap<Object, Object> hashMap = new QizeHashMap<>();
        hashMap.put("a", "毛毛");
        hashMap.put(97, "小丽");
        System.out.println(hashMap.get("a"));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    运行得到的打印结果为 “小丽” ,而不是 “毛毛” ,这就是hash冲突问题。
    在这里插入图片描述
    怎么解决hash冲突问题?
    在JDK 1.7中是通过链表来解决的。
    先判断该index位置 是否有存放键值对(Entry对象),如果取出结果为null,则表示此index位置没有存放数据,则可以直接存放,如果取出的结果为一个Entry对象,则表示此处有hash冲突,需要进行处理。
    故上述“(5)key的hash算法原理的例子”可以优化为如下:

    public class QizeHashMap<K, V> {
        private  Entry[] entrys = new Entry[10000];
    
        class Entry<K, V> {
            K k;//key
            V v;//value
    
            int hash;//key的hash值
            Entry<K, V> next;//Entry的下一个节点
    
            public Entry(K k, V v, int hash) {
                this.k = k;
                this.v = v;
                this.hash = hash;
            }
        }
    
        public  void put(K k, V v){
            //根据key的hashCode 取余entrys.length
            //得到的余数就是该key存放在我们数组中对应的index位置
    
            /**
             * 1、先判断该index位置 是否有存放数据
             * 2、如果取出结果为null,则表示此index位置没有存放数据,则可以直接存放
             * 3、如果取出的结果为一个Entry对象,则表示此处有hash冲突,需要进行处理
             */
            int hash = k.hashCode();
            int index = hash % entrys.length;
            Entry oldEntry = entrys[index];
            if(oldEntry == null){
                entrys[index] = new Entry<>(k, v, hash);
            }else {
                oldEntry.next = new Entry<>(k, v, hash);
            }
    
        }
    
        public V get(K k){
            //如果根据key查询的话,hashCode 取余entrys.length
            int hash = k.hashCode();
            int index = hash % entrys.length;
            for (Entry<K, V> entry = entrys[index]; entry != null; entry = entry.next){
                if(entry.hash == hash && (entry.k == k || entry.k.equals(k))){
                    return entry.v;
                }
            }
            return null;
        }
    
        public static void main(String[] args) {
            QizeHashMap<Object, Object> hashMap = new QizeHashMap<>();
            hashMap.put("a", "毛毛");
            hashMap.put(97, "小丽");
            System.out.println(hashMap.get("a"));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    三、hashCode方法与equals方法

    1. 如果两个对象equals相等,那么这两个对象的HashCode一定也相同;
    2. 如果两个对象的hashCode相同,不代表两个对象就相同,只能说明这两个对象在散列存储结构中,存放于同一个位置;
    3. 如果对象的equals方法被重写,那么对象的hashCode方法也尽量重写;
    4. String的equals方法验证的是两个字符串的值是否一样。

    eg:

    public class Student {
        String name;
        int age;
    
        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Student student = (Student) o;
            return age == student.age &&
                    Objects.equals(name, student.name);
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(name, age);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    public static void main(String[] args) {
        /**
         *  equals 属于 object父类中,默认的情况下 在比较两个对象的内存地址是否相同
         *  两个字符串equals 是比较两个对象的值是否相等,因为String类重写了equals方法
         */
        String str1 = "qize";
        String str2 = "qize";
        System.out.println(str1.equals(str2));
        System.out.println("--------------------");
        Student student1 = new Student("qize",22);
        Student student2 = new Student("qize",22);
        System.out.println(student1.equals(student2));
        System.out.println(student1.hashCode());
        System.out.println(student2.hashCode());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    四、LinkedHashMap集合

    LinkedHashMap 继承自HashMap,它的多种操作都是建立在HashMap操作的基础上的,与HashMap不同的是,LinkedHashMap 维护了一个Entry 的双向链表,保证了插入的Entry 中的顺序。
    eg:

    public static void main(String[] args) {
        LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();
        for(int i=1; i<=100; i++){
            linkedHashMap.put("k:"+i,"v:"+i);
        }
        for (Map.Entry<String, String> entry:linkedHashMap.entrySet()){
            System.out.println(entry.getKey()+","+entry.getValue());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • 相关阅读:
    axios是如何实现的(源码解析)
    sqlplus rlwrap: error: Cannot execute sqlplus: Too many levels of symbolic lin
    深入理解计算机系统学习笔记
    java开发岗位面试
    Oracle EBS fndgfm.jsp error : 您没有访问此文件内容的权限
    wpf 异步等待框
    消息队列的选择与应用
    【unity插件】UGUI的粒子效果(UI粒子)—— Particle Effect For UGUI (UI Particle)
    数据安全:证书和密钥对概念详解
    插值与拟合的区别以及如何选取
  • 原文地址:https://blog.csdn.net/qq_36216193/article/details/127942196