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

基本类型与包装类

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

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

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

常用实现

1、创建

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

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);
	}
}

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));
	}
}

4、删除元素 remove(key)方法:
5、删除所有元素 :clear()方法

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);
		// 删除所有元素
		A.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)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
}

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

(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);
}

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

(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);
}

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

(4) HashMap(Map<? extends K,? extends V> 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);
    }
    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);
            }
        }
    }

HashMap的内部变量

这些是哈希表内部的一些变量:
int DEFAULT_INITIAL_CAPACITY = 1 << 4

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

这里有一个常见的面试问题,即问你为什么HashMap的初始容量是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才会考虑将链表转换成树,也是避免在小容量的时候进行不必要的转换

Node<K,V>[] table

存放元素的数组,容量总是2的幂次方

Set<Map.Entry<K,V>> entrySet

存放具体元素的集合

int size

HashMap中键值对的数量

int modCount

扩容或者结构更改的计数器

int threshold

HashMap进行扩容的阈值,当实际大小超过该值就会进行扩容

float loadFactor

实际负载因子

初始容量和负载因子

这是影响哈希表的两个重要因素

红黑树和链表转化

HashMap的内部数据结构

HashMap内部哈希算法

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

参考文章

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

  • 27
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值