文章目录
前面我们讲完了以Collection为首的单值存储集合类集,那么现在我们就来讲一下以Map为首的双值存储集合,更为官方的定义是将键映射到值的对象集合,即键值对集合,也称为二元偶对象
map为集合中最顶级的双值操作父接口,很多人误以为list,set,map是属于同一级别的,但是实际上,map和Collection是属于同级的,附上连接Java集合之—Collection
此接口定义如下:
public interface Map<K,V>
map的继承树如下所示:
Map
map的特点
1、map集合中的元素都是以键值对的形式进行存储的 key->value
2、map中的key是唯一的,即一个键只能映射一个值,二次映射会进行覆盖
3、map中的值是可以重复的
map常用方法及实例
方法名称 | 方法概述 |
---|---|
void clear() | 清空 Map 集合中的内容 |
boolean containsKey(Object key) | 判断集合中是否存在指定的 key |
boolean containsValue(Object value) | 判断集合中是否存在指定的 value |
Set*<Map.Entry<K,V>>* entrySet() | 将 Map 接口变为 Set 集合 |
V get(Object key) | 根据 key 找到其对应的 value |
boolean isEmpty() | 判断是否为空 |
Set*<K>* keySet() | 将全部的 key 变为 Set 集合 |
Collection*<V>* values() | 将全部的 value 变为 Collection 集合 |
V put(K key,V value) | 向集合中增加内容 |
void putAll(Map<? extends K,? extends V> m) | 增加一组集合 |
V remove(Object key) | 根据 key 删除内容 |
例子
package com.blog.map;
import java.util.*;
/**
* @Author jinhuan
* @Date 2022/4/15 13:39
* Description:(以HashMap为例子,其余map类同样有效)
*/
public class Test01 {
public static void main(String[] args) {
Map map = new HashMap<>();
//V put(K key,V value) 添加一个键值对
/**
* 注意:
* 此处是有返回值的,返回的是以前key对应的值
* 如果该key没有使用过,则返回null
* */
map.put(1,"第1个元素");
map.put(2,"第2个元素");
Object res01 = map.put(3, "第3个元素");
Object res02 = map.put(1, "第4个元素");
System.out.println(res01);//null
System.out.println(res02);//第1个元素
//V get(Object key) 根据key去查询对应的value
System.out.println(map.get(1));//第4个元素
System.out.println(map.get(2));//第2个元素
//void putAll(Map<? extends K,? extends V> m) 添加一组(map类型)数据
Map map01 = new HashMap<>();
map01.putAll(map);
System.out.println("map中的元素有-->"+map); //map中的元素有-->{1=第4个元素, 2=第2个元素, 3=第3个元素}
System.out.println("map01中的元素有-->"+map01); //map01中的元素有-->{1=第4个元素, 2=第2个元素, 3=第3个元素}
//boolean containsKey(Object key) 判断集合中是否含有指定的key
System.out.println(map.containsKey(1) ? "包含该key":"不包含该key");//包含该key
System.out.println(map.containsKey(8) ? "包含该key":"不包含该key");//不包含该key
//boolean containsValue(Object value) 判断集合中是否含有指定的value
System.out.println(map.containsValue("第2个元素") ? "包含该value":"不包含该value");//包含该value
System.out.println(map.containsValue("123") ? "包含该value":"不包含该value");//不包含该value
//V remove(Object key) 根据key删除对应的value
map01.remove(1);
System.out.println("map01中的元素有-->"+map01); //map01中的元素有-->{2=第2个元素, 3=第3个元素}
//void clear() 清空map集合中的所有数据
map01.clear();
System.out.println("map01中的元素有-->"+map01); //map01中的元素有-->{}
//boolean isEmpty() 判断该集合是否为空
System.out.println(map.isEmpty() ? "map集合为空" : "map集合不为空");//map集合不为空
System.out.println(map01.isEmpty() ? "map01集合为空" : "map01集合不为空");//map01集合为空
//Set<K> keySet() 将全部的 key 变为 Set 集合
Set set = map.keySet();
//遍历keySet
for (Object k : set) {
System.out.print(k + "\t");
}
System.out.println();//1 2 3
//Collection<V> values() 将全部的 value 变为 Collection 集合
Collection values = map.values();
for (Object v : values) {
System.out.print(v + "\t");
}
System.out.println();//第4个元素 第2个元素 第3个元素
// Set<Map.Entry<K,V>> entrySet() 将Map接口变为Set集合,此时的键值对将以 key=value的形式 转换为单值存储
Set set1 = map.entrySet();
for (Object o : set1) {
System.out.print(o);
}
System.out.println();//1=第4个元素2=第2个元素3=第3个元素
}
}
HashMap
定义
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
此类继承了 AbstractMap 类,同时可以被克隆,可以被序列化
hashMap是无序存放的
实现原理
HashMap是基于哈希表的对Map的实现,即其数据结构是哈希表,该哈希表的实现设计通过对象数组+链表的的形式,在链表达到一定长度的时候,链表还会转换为二叉树,
哈希哈希,一且和哈希有关的方法,都离不开Object这个根的HashCode方法,下面就先来了解一下这个方法
HashCode
定义:
int hashCode() 返回一个对象的哈希代码值
特点:
1、重写了此方法的类,支持散列表,即哈希表,因为哈希值的作用就是确定存储对象在散列结构中的存储位置
2、hashcode方法返回该对象的哈希码值。支持该方法是为哈希表提供一些优点,例如,java.util.Hashtable 提供的哈希表。
3、在Java应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较 中所用的信息没有被修改。
4、从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
5、如果通过equals方法比较两个对象是相等的,那么这两个对象调用 hashCode 方法都必须生成相同的整数结果。
6、当equals方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,即相等对象必须具有相等的哈希码。
7、两个对象的哈希值相同不代表两个对象是相同的,只能代表他们在散列存储结构中位于同一个哈系桶
哈希表
初始化容量:创建哈希表时对象链表(哈希桶)的数量默认为16;
尺寸:当前hash表中记录的数量;
容量:hash表中桶的数量;
散列(负载)因子:负载因子范围0~1,0表示空的hash表,0.5表示半满的hash表,1表示满(几乎不存在)。小对应着查询效率高,存储容量低;大对应着存储容量高,查询效率低。
负载极限:负载极限是一个0~1之间的数值,决定了hash表的最大填满程度。当hash表的散列因子达到指定负载极限时,hash表会自动成倍地增加容量,并将原有的对象重新分配取模,放入新的桶中。HashSet、HashMap、Hashtable默认的负载极限都是0.75,这个是官方测试后给出的最佳大小。
概念(有点长,但是很有用)
上面已经说了,哈希表的实现是通过对象数组加链表的形式,这个对象数组的默认长度是16(下标对应 0 - 15),数组中的每一个元素都是一个链表,这个链表我们称之为哈希桶
我们存储的对象一定是Object的子类,所有它一定有自己的hashcode,我们存储的方式就是拿着对象调用hashcode的返回值和哈希桶的数量(默认16)进行取模运算,最终得到的结果一定是 0 到 桶的数量-1 (默认0-15)中的一个数值,那么我们就根据最终的取模结果,存储到对象数组对应下标的哈希桶里面,这样存储的效果就是更便于我们进行对象的查找。比如我们要找一个对象,只需要获取它的哈希值,然后取模,就能够确定它存储在对象数组的那一个下标里面,根本不需要去全部遍历整个存储结构,这也是它查询效率高的原因之一。
接下来说链表,当两个元素的哈希值对16取模后得到的结果一样的时候,就被存储到了同一个链表里,即同一个桶里,当哈希桶中元素的个数大于8 的时候,该链表会自动转换为红黑树,更便于我们进行查找,当哈希桶中的数据减少为6 的时候,又会从红黑树转换为链表
重写hashcode以及equals方法
为什么在重写equals方法的时候hashcode方法也要重写呢?
1、用于数据的存储,当两个对象使用哈希值取模运算后发现存储在同一个哈希桶的时候,通过equals和该桶中的元素进行判断,为true则不允许存储,false则可以存储
2、用于数据的使用,当根据key去获取对应的value的时候,如果我们修改了equals规则,而没有重写hashcode,就可能出现一种情况,相等的对象没有相等的哈希值,就导致我们存取key的时候根据哈希值存储到了位置A,但是取得时候却找到了B
HashTable
定义:
Hashtable 是一个最早的 key->value 的操作类,本身是在 JDK 1.0 的时候推出的。其基本操作与 HashMap 是类似的。但是HashMap可以存放null,HashTable不可以,其常用方法和map中介绍一样
注意,整个集合中除了 ArrayList 和 Vector 的区别之外,另外一个最重要的区别就是 HashMap 与 Hashtable 的区别
HashMap | HashTable |
---|---|
线程不安全 | 线程安全 |
默认的初始化大小是16 每次扩充为2倍 | 默认的初始大小为11 每次扩充为2n+1 |
可以允许key和value为空的且存储在首位(数组索引为0处) | key和value都不能为null |
TreeMap
TreeMap 子类是允许 key 进行排序的操作子类,其本身在操作的时候将按照 key 进行排序,另外,key 中的内容可以
为任意的对象,但是要求对象所在的类必须实现 Comparable 接口。
定义
public class TreeMap<K,V>
extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable
TreeMap继承AbstractMap,实现NavigableMap、Cloneable、Serializable三个接口。其中AbstractMap表明TreeMap为一个Map即支持key-value的集合
>
extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable
TreeMap继承AbstractMap,实现NavigableMap、Cloneable、Serializable三个接口。其中AbstractMap表明TreeMap为一个Map即支持key-value的集合