• HashMap学习


    1、hashmap 与 hashtable 的区别

    1. 线程安全
    2. null值
    3. 执行效率

    2、hashmap

    hashmap是java中常用的集合类之一,

    1. 继承自AbstractMap类,实现了 Map、Cloneable、java.io.Serializable 接口。
    2. 使用哈希表来存储键值对(key-value),通过将键值映射成哈希码来进行高效的插入、删除操作。
    3. 具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步。
    java.lang.Object
    	java.util.AbstractMap<K,V>
    		java.util.HashMap<K,V>
    Type Parameters:
    	K - the type of keys maintained by this map
    	V - the type of mapped values
    All Implemented Interfaces:
    	Serializable, Cloneable, Map<K,V>
    Direct Known Subclasses:
    	LinkedHashMap, PrinterStateReasons
    
    public class HashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    基本类型与包装类

    HashMap 中的元素实际上是对象,一些常见的基本类型可以使用它的包装类。

    基本类型对应的包装类表如下:

    基本类型引用类型
    booleanBoolean
    byteByte
    shortShort
    intInteger
    longLong
    floatFloat
    doubleDouble
    charCharacter

    常用实现

    1、创建

    import java.util.HashMap; // 引入 HashMap 类
    HashMap<Integer, String> A = new HashMap<Integer, String>();
    
    • 1
    • 2

    2、添加元素 put() 方法:
    当调用put方法的时候,先通过hashCode() 获取key 的hashCode,然后找到bucket位置,将Entry对象 存储到bucket上面。

    public class Test {
    	public static void main(Strings[] args) {
    		// 创建 HashMap 对象 A
    		HashMap<Integer, String> A = new HashMap<Integer, String>();
    		// 添加键值对
    		A.put(1, "apple");
    		A.put(2, "banana");
    		System.out.println(A);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3、访问元素 get(key) 方法:
    通过get(key)获取对象时,也是通过hashCode()方法 获取key的hashCode,然后得到Entry对象。

    public class Test{
    	public static void main(String[] args) {
    		// 创建 HashMap 对象 A
    		HashMap<Integer, String> A = new HashMap<Integer, String>();
    		// 添加键值对
    		A.put(1, "apple");
    		A.put(2, "banana");
    		// 访问
    		System.out.println(A.get(1));
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    4、删除元素 remove(key)方法:

    public class Test{
    	public static void main(String[] args) {
    		// 创建 HashMap 对象 A
    		HashMap<Integer, String> A = new HashMap<Integer, String>();
    		// 添加键值对
    		A.put(1, "apple");
    		A.put(2, "banana");
    		// 删除
    		A.remove(1);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    5、删除所有元素 clear()方法

    6、计算 HashMap 中元素的数量 size() 方法

    7、 遍历

    1. 可以使用 for-each 来迭代 HashMap 中的元素
    2. 只想获得 key ,使用 KeySet() 方法,然后通过 get(key) 获取对应的 value
    3. 指向获得value,有getValue() 方法.
    // 方法一:
          Iterator iterator = hashMap.keySet().iterator();
           while (iterator.hasNext()){
               String key = (String)iterator.next();
               System.out.println(key+"="+hashMap.get(key));
           }
    // 方法二:
    	Iterator iterator1 = hashMap.entrySet().iterator();
        while (iterator1.hasNext()){
            Map.Entry entry = (Map.Entry) iterator1.next();
            String key = (String) entry.getKey();
            Integer value = (Integer) entry.getValue();
            System.out.println(key+"="+value);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    变量介绍

    该类提供了四种构造方法:
    (1)HashMap()
    Constructs an empty HashMap with the default initial capacity (16) and the default load factor (0.75).

    public HashMap(){
    	this.loadFactor = DEFAULT_LOAD_FACTOR; //all other fields defaulted
    }
    
    • 1
    • 2
    • 3

    这是我们用的比较频繁的一个构造方法了,没有特殊的写法,所有变量均是默认值

    (2) HashMap(int initialCapacity)
    Constructs an empty HashMap with the specified initial capacity and the default load factor (0.75).

    public HashMap(int initialCapacity) {
    	this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
    
    • 1
    • 2
    • 3

    该构造方法传入一个指定容量,然后调用另外一个重载的构造方法

    (3) HashMap(int initialCapacity, float loadFactor)
    Constructs an empty HashMap with the specified initial capacity and load factor.

    public HashMap(int initialCapacity, float loadFactor) {
    	if(initialCapacity < 0)
    		throw new IllegalArgumentException("Illegal initial capacity: " +
                                                   initialCapacity");
        if (initialCapacity > MAXIMUM_CAPACITY)
                initialCapacity = MAXIMUM_CAPACITY;
        if(loadFactor <= 0 || Float.isNaN(loadFactor))
        	throw new IllegalArgumentException("Illegal load factor: " +
                                                   loadFactor);
        this.loadFactor = loadFactor;
        this.threshold = tableSizeFor(initialCapacity);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    该构造方法指定一个容量值和一个负载因子;从方法内部逻辑可以看出容量值小于0将会抛出异常,大于最大容量值就重置为最大值;负载因子 如果小于等于0或者是个非法数字也将会抛出异常;最后一句代码的意思是计算HashMap下次扩容时的阈值,也就是说HashMap并不会等被填满时才会进行扩容,而是达到这个阈值时就开始提前扩容

    (4) HashMap(Map m)
    Constructs a new HashMap with the same mappings as the specified Map.

        public HashMap(Map<? extends K, ? extends V> m) {
            this.loadFactor = DEFAULT_LOAD_FACTOR;
            // 将m中的所有键值对添加到HashMap
            putMapEntries(m, false);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
        final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
            int s = m.size();
            if (s > 0) {
                if (table == null) { // pre-size
                	//数组没有初始化 重新计算阈值
                    float ft = ((float)s / loadFactor) + 1.0F;
                    int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                             (int)ft : MAXIMUM_CAPACITY);
                    if (t > threshold)
                        threshold = tableSizeFor(t);
                }
                else if (s > threshold)
                   //已经初始化 如果大小大于阈值 需要扩容
                    resize();
                //将m中所有键值对添加到本HashMap中
                for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
                    K key = e.getKey();
                    V value = e.getValue();
                    putVal(hash(key), key, value, false, evict);
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    HashMap的内部变量:

    int DEFAULT_INITIAL_CAPACITY = 1 << 4

    内部哈希表初始容量,值为16,值必须是2的幂次方

    int MAXIMUM_CAPACITY = 1 << 30

    哈希表最大容量,一般情况下只要内存够用,哈希表不会出现问题

    float DEFAULT_LOAD_FACTOR = 0.75f

    默认的负载因子,因此初始情况下,当键值对的数量大于 16 * 0.75 = 12 时,就会触发扩容

    int TREEIFY_THRESHOLD = 8

    这个值表示当某个桶中,链表长度大于 8 时,将链表转化成红黑树;如果哈希函数不合理,导致过多的数据碰撞,即使扩容也无法减少箱子中链表的长度,因此将链表转换成红黑树

    int UNTREEIFY_THRESHOLD = 6:

    当哈希表在扩容时,如果桶中的链表长度小于 6,则会由树重新退化为链表

    int MIN_TREEIFY_CAPACITY = 64:

    即哈希表中的桶数量要大于64才会考虑将链表转换成树,也是避免在小容量的时候进行不必要的转换

    初始容量和负载因子

    红黑树和链表转化

    HashMap的内部数据结构

    HashMap内部哈希算法

    以上四节均学习于此文章:
    源码解析-深刻理解Hash HashTable HashMap原理及数据hash碰撞问题

    参考文章

    官网描述
    菜鸟教程
    【深入Java基础】HashMap的基本用法

  • 相关阅读:
    蓝牙技术|物联网的可穿戴设备新工作模式,蓝牙BLE助力新工作模式
    C语言之自定义类型_结构体篇(1)
    vue 引用百度地图
    集合深度学习10—同步类容器
    【同时完成超分和MEF】
    详解设计模式:模版方法模式
    蓝牙耳机热销榜是哪些?盘点性价比蓝牙耳机排行榜
    基于Matlab使用雷达资源管理有效跟踪多个机动目标仿真(附源码)
    文件不小心删除了怎么恢复?
    【Go 基础篇】Go语言标识符解析:命名的艺术与最佳实践
  • 原文地址:https://blog.csdn.net/Daisy_G_Jasmine/article/details/136283704