点击上方「10分钟编程」关注我呦
让我们在一起每天「博学」一点点,成为更好的自己!
Map
我们已经学习了collection集合了,为什么还要学map集合呢?map集合有什么特点呢?
我们先看一些collection集合中一些实现类的特点
Vector与ArrayList访问速度快,但是插入和删除速度慢
LinkdList访问速度慢,但插入和删除速度快
那么,我们想要一种查询、插入和删除都比较不错的数据结构,那么,map就出现了
Map是一种键值(key-value)对集合,集合中的每一个元素都包含一个键对象和一个值对象,其中,键对象不能重复,值对象可以重复,值对象可以是map类型的,
在这篇文章中,我们将主要讲解map接口中的方法功能和其实现类的功能特点,对map接口有一个总体的把握,对实现类深入的讲解将会开设具体的专题讲解。
继承链
1、接口中的方法
1.1查询方法
int size();//返回键值对的数量
boolean isEmpty();//如果集合为空返回true
boolean containsKey(Object key);//检查集合中是否存在指定的key值
boolean containsValue(Object value);//检查集合中是否存在指定的value
V get(Object key);//获取指定的key对象的键值对
1.2添加和删除方法
V put(K key, V value);//在集合中添加key-value键值对
V remove(Object key);//删除指定key值的键值对
void putAll(Map<? extends K, ? extends V> m);//从指定的map中的所有键值对映射到次map中
void clear();//清空集合中的所有键值对
1.3视图表示
通过一个map进行迭代要比Collection复杂,因为Map不提供迭代器,但是提供了三种方法,可以将Map对象的视图作为Collection对象返回
//返回map中的包含所有key的的一个Set视图
Set<K> keySet();
//返回一个map中包含所有value的一个Collections视图
Collection<V> values();
//返回一个map中包含所有映射的一个Set视图
Set<Map.Entry<K, V>> entrySet();
Note:方法KeySet和values返回简单的集合(这些关键字不包含重复元,因此以一个Set对象返回)。
对于Map.Entry<K, V>,其现有的方法有访问key值和value值以及改变value值
K getKey();//获取key值
V getValue();//获取value值
V setValue(V value);//修改value值
1.4 equals和hashCode
equals方法
比较指定的对象和该对象是否相等
它在Object类中比较的是两个对象的地址值是否相等
public boolean equals(Object obj) {
return (this == obj);
}
Note:在不同的类中,都重写了equals方法,比如在String类中,比较的是内容而不是地址。
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
Note:同样的,Math,Integer类中也重写了equals方法,这里就不在赘述
Map中提供了一个equals接口用于比较对象的值,具体的比较要看其实现类中的重写方法
//比较指定的对象这个entry的对象是否相等,如果相等则返回true
boolean equals(Object o);
我们先看一下HashMap中重写的equals方法
public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
很显然,它比较的是对象的内容而非地址
hashCode方法
hashCode是通过hash函数得到的,hash函数是通过某一种算法得到的,如直接地址法、数字分析法、平方取中法等,hashCode就是在hash表中对应的位置
以直接地址法举例
如果我们现在要对0-100岁的人口数字统计表,那么我们对年龄这个关键字就可以直接用年龄的数组作为地址
此时f(key)=key
地址 | 年龄 | 人数 |
---|---|---|
00 | 0 | 500万 |
01 | 1 | 600万 |
02 | 2 | 450万 |
...... | ....... | ....... |
20 | 20 | 1500万 |
...... | ...... | ....... |
当然,我们也可以取关键字的某个线性函数值为散列地址,可以为f(key)=a*key+b (a,b为常数)
那么,hashCode有什么用呢?
想象一下这个场景,我们要在内存中存贮1000个不一样的数据,每当我们存一个数据时,就要遍历所有的数据进行比较,效率很低,当如果每存入一个对象,都调用hashCode方法,得到相应的hashCode值,在具体的实现中会用一个table保存已经存进去的对象的hashCode值,如果table中没有这个hashCode值,就可以直接存进去,不用再进行任何比较了,如果存在该hashCode值,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其他的地址,这样就大大提高了效率。
Object类中hashCode方法是本地方法,直接返回对象的内存地址,具体的实现类重写hashCode方法,我们看一下String类的hashCode方法
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
同样的,Map中也提供了hashCode方法
int hashCode();
以HashMap为例,HashMap中重写了HashCode方法
//hash函数
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
//hashCode函数
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
hashCode方法和equals的关系
如果两个对象equals相等,那么这两个对象的HashCode一定也相同
如果两个对象的HashCode相同,不代表两个对象就相同,只能说明这两个对象在散列的存储结构中,处于同一个位置
2、map接口的实现类
在这篇文章中,我们只是简单分析下每个实现类的特点,后面的文章会分模块具体的讲解
hashMap
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
hashMap除了实现了map接口,还实现了Cloneable,Serializable,继承了AbstractMap类,该类不保证映射的顺序。
hashMap的特点
允许使用null值和null键,键是无序的,但是唯一
值有序,是可以重复的
底层的数据是哈希表,保证了键的唯一
LinkedHashMap
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>
键是有序的并且唯一
值有序,是可以重复的
底层的数据结构是哈希表和链表,哈希表保证了键唯一,链表保证了值是有序的
TreeMap
public class TreeMap<K,V>
extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable
键是可排序的且唯一
值有序,是可以重复的
底层数据结构是自平衡二叉树(红黑树),是可排序的
HashTable
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable
不允许null值和null键,
线程安全,但是效率低
使用哈希表来实现,可以将键值映射到相应的值。
参考文献
[1]程杰.大话数据结构
[2]马克.艾伦.维斯.数据结构与算法分析
笔者组建了技术交流群,关注公众号,欢迎大家加入,一起进步。
在公众号回复success领取独家整理的学习资源
看了这篇文章,你是否「博学」了
「扫码关注我」,每天博学一点点。