Java 集合框架系列(四):Map 接口及其实现类

Map

用于存储键值对,是一个接口,其常见的实现类有 HashMap、Hashtable、TreeMap 等。在该接口中定义的统一操作有:

  1. 添加元素:
 // 用于键值对的绑定,当键不存在是返回null,当键存在是返回旧值。
 V put(K key, V value);
  1. 判断
 // 是否包含键
 boolean containsKey(Object key);
 // 是否包含值
 boolean containsValue(Object value);
 // 是否为空
 boolean isEmpty();
 // 判断两个map是否相同
 boolean equals(Object o);
  1. 获取元素
 // 根据键获取value值,不存在则返回null
 V get(Object key);
 // 获取所有键
 Set<K> keySet();
 // 获取所有的值
 Collection<V> values();
 // 获取所有键值对
 Set<Map.Entry<K,V>> entrySet();
 // 大小
 int size();
  1. 移除元素
// 移除某个键
 V remove(object key);
 // 清空map的内容
 void clear();

Map 接口及其实现类

HashMap

HashMap 是 JDK1.2 版本之后开始增加的,在 1.8 版本之前其底层使用数组+链表的方式来作为哈希表的存储结构,在 1.8 版本及之后改成了数组+链表/红黑树的方式来作为哈希表的存储结构,当链表的长度大于8的时候会将链表转换成一颗红黑树。
在分析 HashMap 的源码之前,先来看看一个键值对放入一个 Map 中应该经过什么步骤:
在这里插入图片描述

  1. 重要成员属性
// 初始容量,必须为2的幂次方,默认为16
 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
 
 // 最大的容量
 static final int MAXIMUM_CAPACITY = 1 << 30;
 
 // 默认的负载因子
 static final float DEFAULT_LOAD_FACTOR = 0.75f;
 
 // 链表的长度的阈值,大于该长度会转化成红黑树
 static final int TREEIFY_THRESHOLD = 8;
 // 红黑树收缩成链表的阈值,即当树节点为6个时会将树转换成红黑树
 static final int UNTREEIFY_THRESHOLD = 6;
 // 哈希表的桶大于 64 时才会将链表转换成树结构
 static final int MIN_TREEIFY_CAPACITY = 64;
 
 // 用数组结构保存哈希表
 transient Node<K,V>[] table;
 // 键值对集合,可以用于键值对的遍历
 transient Set<Map.Entry<K,V>> entrySet;
 // 键值对的数量
 transient int size;
 // 修改容器的次数
 transient int modCount;
 // 容量阈值
 int threshold;
 // 负载因子
 final float loadFactor;
  1. 构造方法
public HashMap() {
   
     this.loadFactor = DEFAULT_LOAD_FACTOR; // 设置为默认的负载因子
 }
 
 // 设置初始化容量,在阿里的开发手册中要求在创建 HashMap 时要指定初始化容量的大小
 public HashMap(int initialCapacity) {
   
     this(initialCapacity, DEFAULT_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;
     // 这里会将初始化容量转化成第一个比其大的一个2的幂次方的数
     this.threshold = tableSizeFor(initialCapacity);
     // n = -1 >>> Integer.numberOfLeadingZeros(cap - 1);
     // numberOfLeadingZeros可以获取前导0的数量。
 }
  1. 关键操作
  • put 添加元素方法,put 方法只是一个入口,实际添加元素的方法为 putVal。
// 不存在映射则会关联值,存在则直接替换值
 public V put(K key, V value) {
   
     return putVal(hash(key), key, value, false, true);
 }
 // 计算hash值,从这里可以看出 HashMap 的 key 可以为null,其hash值为0 
 // 面试可能会问。
 // 计算方式为获取 hashCode 之后再进行一次散列,可以打乱进一步的打乱值。
 static final int hash(Object key) {
   
     int h;
     return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
 }
 /** 
     hash – 密钥的散列
     key——钥匙
     value – 要放置的值
     onlyIfAbsent – 如果为真,则不更改现有值
     evict – 如果为 false,则表处于创建模式。
     返回:以前的值,如果没有,则为 null
 */
 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                boolean evict) {
   
     Node<K,V>[] tab; 
     Node<K,V> p; 
     int n, i;
     if ((tab = table) == null || (n = tab.length) == 0)
         // 如果 table 为空,则进行第一次的 resize 操作,分配空间
         // 因此 HashMap 是延迟到添加元素的时候才分配空间的
         n = (tab = resize()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值