集合--Map

 点击上方「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

地址年龄人数
000500万
011600万
022450万
....................
20201500万
...................

当然,我们也可以取关键字的某个线性函数值为散列地址,可以为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领取独家整理的学习资源

看了这篇文章,你是否「博学」了

「扫码关注我」,每天博学一点点。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值