数据结构——Map接口及其实现类

目录

一、HashMap

二、HashTable

三、ConcurrentHashMap

四、TreeMap


Map集合利用“key-value”的方式进行存储。

Map接口的主要实现类有:HashMap,HashTable,TreeMap

  • HashMap 线程不安全
  • TreeMap 线程不安全
  • HashTable 线程安全

Map接口提供了一些公共方法:

public interface Map<K,V> {

    //返回Map中的key--value的数目
    int size();

    //如果Map不包含任何key--value,则返回 true
    boolean isEmpty();

    //如果Map中包含指定key的映射,则返回true
    boolean containsKey(Object key);

    //如果此Map将一个或多个键映射到指定值,则返回 true
    boolean containsValue(Object value);

    //返回与指定键关联的值
    V get(Object key);

    //将指定值与指定键相关联
    V put(K key, V value);

    //从Map中删除键和关联的值
    V remove(Object key);

    //将指定Map中的所有映射复制到此map
    void putAll(java.util.Map<? extends K, ? extends V> m);

    //从Map中删除所有映射
    void clear();

    //返回Map中所包含键的Set集合
    Set<K> keySet();

    //返回 map 中所包含值的 Collection集合。
    Collection<V> values();

    //返回Map中所包含映射的Set视图。Set中的每个元素都是一个 Map.Entry 对象
    Set<java.util.Map.Entry<K, V>> entrySet();

    //比较指定对象与此 Map 的等价性
    boolean equals(Object o);

    //返回此 Map 的哈希码
    int hashCode();

    //Map集合中存储key--value的对象Entry,在Map集合内形成数组结构
    interface Entry<K,V> {

        V getValue();

        V setValue(V value);

        boolean equals(Object o);

        int hashCode();
    }
}

哈希表(HashTable,也叫散列表):

是根据关键码值(key value)而直接进行访问的一种数据结构,也就是说他通过把key-value映射到表中的一个位置来访问记录加快查找速度。

Hashtable 是早期 Java 类库提供的一个哈希表实现,本身是同步的,不支持 null 键和值,由于同步导致的性能开销,所以已经很少被推荐使用。

HashMap 是应用更加广泛的哈希表实现,行为上大致上与 HashTable 一致,主要区别在于 HashMap 不是同步的,支持 null 键和值等。通常情况下,HashMap 进行 put 或者 get 操作,可以达到常数时间的性能,所以它是绝大部分利用键值对存取场景的首选,比如,实现一个用户 ID 和用户信息对应的运行时存储结构。

TreeMap 则是基于红黑树的一种提供顺序访问的 Map,和 HashMap 不同,它的 get、put、remove 之类操作都是 O(log(n))的时间复杂度,具体顺序可以由指定的 Comparator 来决定,或者根据键的自然顺序来判断。

一、HashMap

HashMap是基于哈希表,底层结构是由数组来实现,添加到集合中的元素通过“key-value”的形式保存到数组中。

  • HashMap将键值对key-value包装成一个实例存放在数组中,实体就是map接口中的entry。
  • HashMap通过hash算法来确定每个键值对的位置,实现对于元素的增加,删除,修改等操作。HashMap 的性能表现非常依赖于哈希码的有效性,请务必掌握 hashCode 和 equals的一些基本约定
  • HashMap通过 链式地址法 来解决“哈希冲突”;(Java8以后,在哈希冲突过多的时候,为了降低元素查询的时间复杂度,会将链表改为红黑树结果进行存储。树化的本质原因

HashMap的主要特点:

  • 允许null值和null键
  • 动态扩容(扩容机制与ArrayList相同)
  • 不存在重复的元素
  • 无序的

HashMap主要实现的接口:

  • Map接口:得到了map中定义的所有接口
  • Cloneable接口:实现Cloneable接口并重写Object类中的clone方法,Cloneable接口中没有任何的实现方法,它属于一个标识性接口。
  • Serializable接口:可以进行序列化,通过序列化后进行传输,典型应用就是hessian协议。

HashMap的源码:

public class HashMap<K,V> extends AbstractMap<K,V> 
        implements Map<K,V>, Cloneable, Serializable {

    //hashMap中的数组初始化大小:1 << 4=2^4=16
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;

    //1<<30 表示1左移30位,每左移一位乘以2,所以就是1*2^30=1073741824。
    static final int MAXIMUM_CAPACITY = 1 << 30;

    //默认装载因子:
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    //HashMap默认初始化的空数组:
    static final java.util.HashMap.Entry<?,?>[] EMPTY_TABLE = {};

    //HashMap中底层保存数据的数组:HashMap其实就是一个Entry数组
    transient java.util.HashMap.Entry<K,V>[] table = (java.util.HashMap.Entry<K,V>[]) EMPTY_TABLE;

    //Hashmap中元素的个数:
    transient int size;

    //threshold:等于capacity * loadFactory,决定了HashMap能够放进去的数据量
    int threshold;

    //loadFactor:装载因子,默认值为0.75,它决定了bucket填充程度;
    final float loadFactor;

    //HashMap被操作的次数:
    transient int modCount;

}

HashMap的相关问题:

1、HashMap的底层数据结构?

HashMap最终存储元素的是Entry[]数组,其中的对象就是Entry对象。

2、HashMap是如何进行扩容的,并且为什么每次扩容都必须是2的整数次幂?

HashMap包含三个和扩容相关的成员变量:

  • table 就是底层用于保存数据的数组,默认情况下会先赋值空数组
  • DEFAULT_INITIAL_CAPACITY 初始化大小 ,在首次进行put的时候,才会进行数组的初始化,建立一个容量为16的数组
  • DEFAULT_LOAD_FACTOR 装载因子:扩容的关键参数,HashMap会在元素达到一定数量的时候,对数组进行扩容。这个一定数量就是有装载因子决定的,数组大小*DEFAULT_LOAD_FACTOR。第一次进行扩容的数量就是16*0.75=12个。

扩容的过程:

hashMap的长度一定是2的N次幂,对于Hash值的计算,hashMap中采用的是 与操作,相比于 取模运算 效率更高。

  • 当length为2的N次方的时候,length一定是偶数,这样length-1一定是奇数,当奇数转换成二机制数的时候,最后一位永远是1,那么HashMap中每一个位置都是奇数,当通过Hash值 与 length-1 进行与操作的时候,结果可能是偶数,也可能是奇数,因此散列性比较好,可以有效的降低哈希冲突
  • 当length为奇数是的时候,length-1 就一定是偶数,当偶数转换成二机制的时候,最后一位就是0,任何值和0进行与运算,结果都会是 0 ,因此无论是hash值是啥,进行与操作后结果都是偶数,从而造成一半的数组位都是浪费的。从而增加 哈希冲突的概率,从而降低HashMap的性能。

二、HashTable

HashTable与HashMap的结构一致,都是哈希表实现。

与HashMap不同的是,在HashTable中,所有的方法都加上了synchronized锁,用锁来实现线程的安全性。由于synchronized锁加在了HashTable的每一个方法上,所以这个锁就是HashTable本身--this。因而效率不高。

HashTable的特点:

  • 线程安全的,单线程集合
  • 速度快
  • 不允许null键和null值

HashTable逐渐被concurrentHashMap取代,但是Hashtable的子类Properties依然沿用,Properties集合也是唯一一个和IO流相结合的集合。


三、ConcurrentHashMap

ConcurrentMap是一个接口,支持并发访问的Map的集合。在Map接口上增加了4个扩展方法。主要的实现类就是ConcurrentHashMap

ConcurrentHashMap是一个线程安全并且高效的HashMap。

public interface ConcurrentMap<K, V> extends Map<K, V> {

    //插入元素
    V putIfAbsent(K key, V value);

    //移除元素
    boolean remove(Object key, Object value);

    //替换元素
    boolean replace(K key, V oldValue, V newValue);

    //替换元素
    V replace(K key, V value);
}

ConcurrentHashMap的底层结构

四、TreeMap

TreeMap的键值对是存放在红黑树中的,key的顺序通过红黑树的自平衡实现的。

参考文章-HashMap
参考文章-ConcurrentHashMap
参考文章-TreeMap

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值