• Map 实现类之一:HashMap


    Map 实现类之一:HashMap
    HashMap是 Map 接口 使用频率最高的实现类。
    允许使用null键和null值,与HashSet一样,不保证映射的顺序。
    所有的key构成的集合是Set:无序的、不可重复的。所以,key所在的类要重写:
    equals()和hashCode()
    所有的value构成的集合是Collection:无序的、可以重复的。所以,value所在的类
    要重写:equals()
    一个key-value构成一个entry
    所有的entry构成的集合是Set:无序的、不可重复的
    HashMap 判断两个 key 相等的标准是:两个 key 通过 equals() 方法返回 true,
    hashCode 值也相等。
    HashMap 判断两个 value 相等的标准是:两个 value 通过 equals() 方法返回 true。

     

     HashMap 源码中的重要常量

    DEFAULT_INITIAL_CAPACITY : HashMap的默认容量,16
    MAXIMUM_CAPACITY : : HashMap的最大支持容量,2^30
    DEFAULT_LOAD_FACTOR :HashMap的默认加载因子
    TREEIFY_THRESHOLD :Bucket中链表长度大于该默认值,转化为红黑树
    UNTREEIFY_THRESHOLD :Bucket中红黑树存储的Node小于该默认值,转化为链表
    MIN_TREEIFY_CAPACITY :桶中的Node被树化时最小的hash表容量。(当桶中Node的
    数量大到需要变红黑树时,若hash表容量小于MIN_TREEIFY_CAPACITY时,此时应执行
    resize扩容操作这个MIN_TREEIFY_CAPACITY的值至少是TREEIFY_THRESHOLD的4
    倍。)
    table :存储元素的数组,总是2的n次幂
    entrySet: :存储具体元素的集
    size :HashMap中存储的键值对的数量
    modCount :HashMap扩容和结构改变的次数。
    threshold :扩容的临界值,=容量*填充因子
    loadFactor: :填充因子

    HashMap 的存储: 结构:JDK 1.8 之前

     HashMap的内部存储结构其实是 数组和链表的结合。当实例化一个HashMap时,
    系统会创建一个长度为Capacity的Entry数组,这个长度在哈希表中被称为容量
    (Capacity),在这个数组中可以存放元素的位置我们称之为“桶”(bucket),每个
    bucket都有自己的索引,系统可以根据索引快速的查找bucket中的元素。
     每个bucket中存储一个元素,即一个Entry对象,但每一个Entry对象可以带一个引
    用变量,用于指向下一个元素,因此,在一个桶中,就有可能生成一个Entry链。
    而且新添加的元素作为链表的head。
     添加元素的过程:
    向HashMap中添加entry1(key,value),需要首先计算entry1中key的哈希值(根据
    key所在类的hashCode()计算得到),此哈希值经过处理以后,得到在底层Entry[]数
    组中要存储的位置i。如果位置i上没有元素,则entry1直接添加成功。如果位置i上
    已经存在entry2(或还有链表存在的entry3,entry4),则需要通过循环的方法,依次
    比较entry1中key和其他的entry。如果彼此hash值不同,则直接添加成功。如果
    hash值不同,继续比较二者是否equals。如果返回值为true,则使用entry1的value
    去替换equals为true的entry的value。如果遍历一遍以后,发现所有的equals返回都
    为false,则entry1仍可添加成功。entry1指向原有的entry元素。

    HashMap 的扩容
    当HashMap中的元素越来越多的时候,hash冲突的几率也就越来越高,因为数组的
    长度是固定的。所以为了提高查询的效率,就要对HashMap的数组进行扩容,而在
    HashMap数组扩容之后,最消耗性能的点就出现了:原数组中的数据必须重新计算
    其在新数组中的位置,并放进去,这就是resize。
    那么HashMap 什么时候进行扩容呢 ?
    当HashMap中的元素个数超过数组大小(数组总大小length,不是数组中个数
    size)*loadFactor 时 , 就 会 进 行 数 组 扩 容 , loadFactor 的 默 认 值
    (DEFAULT_LOAD_FACTOR)为0.75,这是一个折中的取值。也就是说,默认情况
    下,数组大小(DEFAULT_INITIAL_CAPACITY)为16,那么当HashMap中元素个数
    超过16*0.75=12(这个值就是代码中的threshold值,也叫做临界值)的时候,就把
    数组的大小扩展为 2*16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,
    而这是一个非常消耗性能的操作,所以如果我们已经预知HashMap中元素的个数,
    那么预设元素的个数能够有效的提高HashMap的性能。

    HashMap 的存储: 结构:JDK 1.8

     HashMap的内部存储结构其实是 数组+ 链表+ 树 的结合。当实例化一个
    HashMap时,会初始化initialCapacity和loadFactor,在put第一对映射关系
    时,系统会创建一个长度为initialCapacity的Node数组,这个长度在哈希表
    中被称为容量(Capacity),在这个数组中可以存放元素的位置我们称之为
    “桶”(bucket),每个bucket都有自己的索引,系统可以根据索引快速的查
    找bucket中的元素。
     每个bucket中存储一个元素,即一个Node对象,但每一个Node对象可以带
    一个引用变量next,用于指向下一个元素,因此,在一个桶中,就有可能
    生成一个Node链。也可能是一个一个TreeNode对象,每一个TreeNode对象
    可以有两个叶子结点left和right,因此,在一个桶中,就有可能生成一个
    TreeNode树。而新添加的元素作为链表的last,或树的叶子结点。

    那么HashMap 什么时候进行扩容和树形化呢 ?
    当HashMap中的元素个数超过数组大小(数组总大小length,不是数组中个数
    size)*loadFactor 时 , 就 会 进 行 数 组 扩 容 , loadFactor 的 默 认 值
    (DEFAULT_LOAD_FACTOR)为0.75,这是一个折中的取值。也就是说,默认
    情况下,数组大小(DEFAULT_INITIAL_CAPACITY)为16,那么当HashMap中
    元素个数超过16*0.75=12(这个值就是代码中的threshold值,也叫做临界值)
    的时候,就把数组的大小扩展为 2*16=32,即扩大一倍,然后重新计算每个元
    素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知
    HashMap中元素的个数,那么预设元素的个数能够有效的提高HashMap的性能。
    当HashMap中的其中一个链的对象个数如果达到了8个,此时如果capacity没有
    达到64,那么HashMap会先扩容解决,如果已经达到了64,那么这个链会变成
    树,结点类型由Node变成TreeNode类型。当然,如果当映射关系被移除后,
    下次resize方法时判断树的结点个数低于6个,也会把树再转为链表。

    代码测试学习:

    @Test
        public void test1(){
            Map map = new HashMap();
    //        map = new Hashtable();
            map.put(null,123);//Hashmap中的键值对可以为空
    
        }

    无报错:

    @Test
        public void test2(){
            Map map = new HashMap();
            map = new LinkedHashMap();
            map.put(123,"AA");
            map.put(345,"BB");
            map.put(12,"CC");
    
            System.out.println(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中的所有数据
    */
    @Test
        public void test3(){
            Map map = new HashMap();
            //添加
            map.put("AA",123);
            map.put(45,123);
            map.put("BB",56);
            //修改
            map.put("AA",87);
    
            System.out.println(map);
    
            Map map1 = new HashMap();
            map1.put("CC",123);
            map1.put("DD",123);
    
            map.putAll(map1);
    
            System.out.println(map);
    
            //remove(Object key)
            Object value = map.remove("CC");
            System.out.println(value);
            System.out.println(map);
    
            //clear()
            map.clear();//与map = null操作不同
            System.out.println(map.size());
            System.out.println(map);
        }

    /*
     元素查询的操作:
     Object get(Object key):获取指定key对应的value
     boolean containsKey(Object key):是否包含指定的key
     boolean containsValue(Object value):是否包含指定的value
     int size():返回map中key-value对的个数
     boolean isEmpty():判断当前map是否为空
     boolean equals(Object obj):判断当前map和参数对象obj是否相等
         */
        @Test
        public void test4(){
            Map map = new HashMap();
            map.put("AA",123);
            map.put(45,123);
            map.put("BB",56);
            // Object get(Object key)
            System.out.println(map.get(45));
            //containsKey(Object key)
            boolean isExist = map.containsKey("BB");
            System.out.println(isExist);
    
            isExist = map.containsValue(123);
            System.out.println(isExist);
    
            map.clear();
    
            System.out.println(map.isEmpty());
    
        }

    /*
    元视图操作的方法:
    Set keySet():返回所有key构成的Set集合
    Collection values():返回所有value构成的Collection集合
    Set entrySet():返回所有key-value对构成的Set集合
    */
     @Test
        public void test5(){
            Map map = new HashMap();
            map.put("AA",123);
            map.put(45,1234);
            map.put("BB",56);
    
            //遍历所有的key集:keySet()
            Set set = map.keySet();
                Iterator iterator = set.iterator();
                while(iterator.hasNext()){
                    System.out.println(iterator.next());
            }
            System.out.println();
            //遍历所有的value集:values()
            Collection values = map.values();
            for(Object obj : values){
                System.out.println(obj);
            }
            System.out.println();
            //遍历所有的key-value
            //方式一:entrySet()
            Set entrySet = map.entrySet();
            Iterator iterator1 = entrySet.iterator();
            while (iterator1.hasNext()){
                Object obj = iterator1.next();
                //entrySet集合中的元素都是entry
                Map.Entry entry = (Map.Entry) obj;
                System.out.println(entry.getKey() + "---->" + entry.getValue());
    
            }
            System.out.println();
            //方式二:
            Set keySet = map.keySet();
            Iterator iterator2 = keySet.iterator();
            while(iterator2.hasNext()){
                Object key = iterator2.next();
                Object value = map.get(key);
                System.out.println(key + "=====" + value);
    
            }
    
        }

    |----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类型
    *
    *
    * HashMap的底层:数组+链表 (jdk7及之前)
    * 数组+链表+红黑树 (jdk 8)

    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中的所有数据
    元素查询的操作:
    Object get(Object key):获取指定key对应的value
    boolean containsKey(Object key):是否包含指定的key
    boolean containsValue(Object value):是否包含指定的value
    int size():返回map中key-value对的个数
    boolean isEmpty():判断当前map是否为空
    boolean equals(Object obj):判断当前map和参数对象obj是否相等
    元视图操作的方法:
    Set keySet():返回所有key构成的Set集合
    Collection values():返回所有value构成的Collection集合
    Set entrySet():返回所有key-value对构成的Set集合

    *总结:常用方法:
    * 添加:put(Object key,Object value)
    * 删除:remove(Object key)
    * 修改:put(Object key,Object value)
    * 查询:get(Object key)
    * 长度:size()
    * 遍历:keySet() / values() / entrySet()
    不积跬步,无以至千里;不积小流,无以成江海。
  • 相关阅读:
    通过串口抓取图片
    Qt也有垃圾回收(通过QScopedPointer实现),下决心在项目里使用QScopedPointer,省了太多事情了,而且更安全!!
    IOS端 margin-top 和 margin-bottom 使用负数时的区别
    使用ROME解析rss,如何获取icon图标
    SVG图片如何调整大小和颜色
    Js点击触发Css3的动画Animations、过渡Transitions效果
    如何判断是否为同一个App,Ionic3如何修改包名
    如何使用JPQL写纯SQL语句
    为何在新线程中使用注解获取不到Spring管理的Bean
    Ionic的NavController 和ModalController 的区别
  • 原文地址:https://www.cnblogs.com/CCTVCHCH/p/14807196.html
Copyright © 2020-2023  润新知