zoukankan      html  css  js  c++  java
  • 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()
    不积跬步,无以至千里;不积小流,无以成江海。
  • 相关阅读:
    如何:为 Silverlight 客户端生成双工服务
    Microsoft Sync Framework 2.1 软件开发包 (SDK)
    Windows 下的安装phpMoAdmin
    asp.net安全检测工具 Padding Oracle 检测
    HTTP Basic Authentication for RESTFul Service
    Windows系统性能分析
    Windows Server AppFabric Management Pack for Operations Manager 2007
    Mongo Database 性能优化
    服务器未能识别 HTTP 标头 SOAPAction 的值
    TCP WAIT状态及其对繁忙的服务器的影响
  • 原文地址:https://www.cnblogs.com/CCTVCHCH/p/14807196.html
Copyright © 2011-2022 走看看