Java中Map集合实现类HashMap、Hashtable、WeakHashMap、EnumMap

最近发现map集合不简单啊,整理一下子(虽然都是百度找,但这不是重点,重点是需要重新过一遍Map集合)。

关系图

虚线箭头表示实现,实线箭头表示继承
在这里插入图片描述

图中可以看到,所有Map的实现类都继承Map接口,其中抽象类AbstractMap是Map接口的一部分实现,而Map的所有实现类都继承了AbsctractMap这个抽象类

Map

将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值

删除该Map对象中所有键值对;
void clear();

查询Map中是否包含指定的key值;
boolean containsKey(Object key);

查询Map中是否包含一个或多个value;
boolean containsValue(Object value);

返回map中包含的键值对所组成的Set集合,每个集合都是Map.Entry对象。
Set entrySet();

返回指定key对应的value,如果不包含key则返回null;
Object get();

查询该Map是否为空;
boolean isEmpty();

返回Map中所有key组成的集合;
Set keySet();

返回该Map里所有value组成的Collection。
Collection values();

添加一个键值对,如果集合中的key重复,则覆盖原来的键值对;
Object put(Object key,Object value);

将Map中的键值对复制到本Map中;
void putAll(Map m);

删除指定的key对应的键值对,并返回被删除键值对的value,如果不存在,则返回null;
Object remove(Object key);

删除指定键值对,删除成功返回trueboolean remove(Object key,Object value);

返回该Map里的键值对个数;
int size();

Map中包括一个内部类Entry,该类封装一个键值对,常用方法:

返回该Entry里包含的key值;
Object getKey();

返回该Entry里包含的value值;
Object getvalue();

设置该Entry里包含的value值,并设置新的value值;
Object setValue(V value);

HashMap

1、使用 HashMap定义的 Map集合是无序存放的。
2、如果发现重复的 key会将新的数据替换掉已有的数据。
3、使用 HashMap子类保存数据时,key或 value可以保存为null。
4、HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap。

HashMap原理
在数据量小的(JDK8后阈值为8)时候,HashMap是按照链表的模式存储的;当数据量变大之后,为了进行快速查找,会将这个链表变为红黑树(均衡二叉树)来进行保存,用hash来进行查找。

HashMap的几个概念:
容量:大于初始容量参数的最小2次幂,默认为16;
负载因子:HashMap扩容条件的一个参数,默认为0.75f;
阈值:容量与负载因子的乘积;

当元素个数为0时:
当初始容量有值时,初始阈值等于最大容量;
当初始容量没有值时,初始阈值为0,容量为为16;
当元素个数不为0时:
阈值均满足容量与负载因子的乘积。

测试源码

public static void main(String[] args) throws Exception {
   //指定初始容量15来创建一个HashMap
   HashMap m = new HashMap(0);
   //获取HashMap整个类
   Class<?> mapType = m.getClass();
   //获取指定属性,也可以调用getDeclaredFields()方法获取属性数组
   Field threshold = mapType.getDeclaredField("threshold");
   //将目标属性设置为可以访问
   threshold.setAccessible(true);
   //获取指定方法,因为HashMap没有容量这个属性,但是capacity方法会返回容量值
   Method capacity = mapType.getDeclaredMethod("capacity");
   //设置目标方法为可访问
   capacity.setAccessible(true);
   //打印刚初始化的HashMap的容量、阈值和元素数量
   System.out.println("初始容量:"+capacity.invoke(m)+"初始阈值:"+threshold.get(m)+" 元素数量:"+m.size());


   for (int i = 0; i < 17; i++) {
      m.put(i, i);
      //动态监测HashMap的容量、阈值和元素数量
      System.out.println("容量:"+capacity.invoke(m)+" 阈值:"+threshold.get(m)+" 元素数量:"+m.size());
   }
}


1、创建一个初始容量为0的HashMap的容量、阈值情况以及put新元素之后的容量、阈值情况
new HashMap(0);
初始容量:1 初始阈值:1 元素数量:0
容量:2 阈值:1 元素数量:1
容量:4 阈值:3 元素数量:2
容量:4 阈值:3 元素数量:3
容量:8 阈值:6 元素数量:4
容量:8 阈值:6 元素数量:5
容量:8 阈值:6 元素数量:6
容量:16 阈值:12 元素数量:7
容量:16 阈值:12 元素数量:8
容量:16 阈值:12 元素数量:9
容量:16 阈值:12 元素数量:10
容量:16 阈值:12 元素数量:11
容量:16 阈值:12 元素数量:12
容量:32 阈值:24 元素数量:13

2、创建一个初始容量为3的HashMap的容量、阈值情况以及put新元素之后的容量、阈值情况
new HashMap(3);
初始容量:4 初始阈值:4 元素数量:0
容量:4 阈值:3 元素数量:1
容量:4 阈值:3 元素数量:2
容量:4 阈值:3 元素数量:3
容量:8 阈值:6 元素数量:4
容量:8 阈值:6 元素数量:5
容量:8 阈值:6 元素数量:6
容量:16 阈值:12 元素数量:7
容量:16 阈值:12 元素数量:8
容量:16 阈值:12 元素数量:9
容量:16 阈值:12 元素数量:10
容量:16 阈值:12 元素数量:11
容量:16 阈值:12 元素数量:12
容量:32 阈值:24 元素数量:13
容量:32 阈值:24 元素数量:14
容量:32 阈值:24 元素数量:15

2、创建一个默认初始容量的HashMap的容量、阈值情况以及put新元素之后的容量、阈值情况
new HashMap();
初始容量:16 初始阈值:0 元素数量:0
容量:16 阈值:12 元素数量:1
容量:16 阈值:12 元素数量:2
容量:16 阈值:12 元素数量:3
容量:16 阈值:12 元素数量:4
容量:16 阈值:12 元素数量:5
容量:16 阈值:12 元素数量:6
容量:16 阈值:12 元素数量:7
容量:16 阈值:12 元素数量:8
容量:16 阈值:12 元素数量:9
容量:16 阈值:12 元素数量:10
容量:16 阈值:12 元素数量:11
容量:16 阈值:12 元素数量:12
容量:32 阈值:24 元素数量:13
容量:32 阈值:24 元素数量:14

Hashtable

HashTable的操作几乎和HashMap一致,主要的区别在于HashTable为了实现多线程安全,在几乎所有的方法上都加上了synchronized锁,而加锁的结果就是HashTable操作的效率十分低下,现在基本已被淘汰,单线程转为使用HashMap,多线程使用ConcurrentHashMap。

HashTable与HashMap对比
(1)HashTable基于Dictionary类,HashMap是基于AbstractMap
(2)线程安全:HashMap是线程不安全的类,多线程下会造成并发冲突,但单线程下运行效率较高;HashTable是线程安全的类,很多方法都是用synchronized修饰,但同时因为加锁导致并发效率低下,单线程环境效率也十分低;
(3)插入null:HashMap允许有一个键为null,允许多个值为null;但HashTable不允许键或值为null;
(4)容量:HashMap底层数组长度必须为2的幂,这样做是为了hash准备,默认为16;而HashTable底层数组长度可以为任意值,这就造成了hash算法散射不均匀,容易造成hash冲突,默认为11;

WeakHashMap(说实话,没用过!!!)

WeakHashMap 继承于AbstractMap,实现了Map接口。
1、和HashMap一样,WeakHashMap 也是一个散列表,它存储的内容也是键值对(key-value)映射,而且键和值都可以是null。
2、WeakHashMap的键是“弱键”。在 WeakHashMap 中,当某个键不再正常使用时,会被从WeakHashMap中被自动移除。更精确地说,对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。某个键被终止时,它对应的键值对也就从映射中有效地移除。

这个“弱键”的原理:

大致上通过WeakReference和ReferenceQueue实现的。 WeakHashMap的key是“弱键”,即是WeakReference类型的;ReferenceQueue是一个队列,它会保存被GC回收的“弱键”。

实现步骤如下:

(1) 新建WeakHashMap,将“键值对”添加到WeakHashMap中。
实际上,WeakHashMap是通过数组table保存Entry(键值对);每一个Entry实际上是一个单向链表,即Entry是键值对链表。
(2) 当某“弱键”不再被其它对象引用,并被GC回收时。在GC回收该“弱键”时,这个“弱键”也同时会被添加到ReferenceQueue(queue)队列中。
(3) 当下一次我们需要操作WeakHashMap时,会先同步table和queue。table中保存了全部的键值对,而queue中保存被GC回收的键值对;同步它们,就是删除table中被GC回收的键值对。

测试源码

public static void main(String[] args) {
    Map<Thread, String> weakHashMap = new WeakHashMap<Thread, String>();

    Thread thread1 = new Thread();
    Thread thread2 = new Thread();
    weakHashMap.put(thread1, "thread1");
    weakHashMap.put(thread2, "thread2");
    thread1 = null;
    System.gc();

    for(Entry<Thread, String> entry:weakHashMap.entrySet()){
        System.out.println(entry.getValue());
    }

    Map<Thread, String> hashMap = new HashMap<Thread, String>();

    Thread thread3 = new Thread();
    Thread thread4 = new Thread();
    hashMap.put(thread3, "thread3");
    hashMap.put(thread4, "thread4");
    thread3 = null;
    System.gc();

    for(Entry<Thread, String> entry:hashMap.entrySet()){
        System.out.println(entry.getValue());
    }
}

这里我主动的用垃圾回收进行了处理,得出的结果是:
thread2
thread4
thread3

可以看出WeakHashMap中的thread1被垃圾回收了。但是HashMap的thread3却没有回收。弱引用关系当垃圾回收发生的时候,如果键为null那么就会被回收。

EnumMap(说实话,也没用过!!!)

EnumMap是专门为枚举类型量身定做的Map实现。虽然使用其它的Map实现(如HashMap)也能完成枚举类型实例到值得映射,但是使用EnumMap会更加高效:它只能接收同一枚举类型的实例作为键值,并且由于枚举类型实例的数量相对固定并且有限,所以EnumMap使用数组来存放与枚举类型对应的值。这使得EnumMap的效率非常高。EnumMap在内部使用枚举类型的ordinal()得到当前实例的声明次序,并使用这个次序维护枚举类型实例对应值在数组的位置。

1、Map接口的实现,其key-value映射中的key是Enum类型;
2、其原理就是一个对象数组,数组的下标索引就是根据Map中的key直接获取,即枚举中的ordinal值;
3、效率比HashMap高,可以直接获取数组下标索引并访问到元素;
4、EnumMap的key不允许为null,value可以为null,按照key在enum中的顺序进行保存,非线程安全。

测试源码

//创建枚举类
public enum Color{
    RED,BLUE,BLACK,YELLOW,GREEN;
}

public static void main(String[] args) {
    EnumMap<Color,String> map = new EnumMap<>(Color.class);
    EnumMap<Color,String> map = new EnumMap<>(Color.class);
    map.put(Color.YELLOW, "黄色");
    map.put(Color.RED, "红色");
    map.put(Color.BLUE, null);
//  map.put(null, "无");   //会报NullPonitException的错误
    map.put(Color.BLACK, "黑色");
    map.put(Color.GREEN, "绿色");

    for(Map.Entry<Color,String> entry:map.entrySet())
    {
        System.out.println(entry.getKey()+":"+entry.getValue());
    }
    System.out.println(map);
}

运行结果:
RED:红色
BLUE:null
BLACK:黑色
YELLOW:黄色
GREEN:绿色
{RED=红色, BLUE=null, BLACK=黑色, YELLOW=黄色, GREEN=绿色}

转载于:https://www.ycblog.top/article?articleId=132&pageNum=1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: JavaMap接口提供了一种将键映射到值的对象。其一种实现HashMap,它使用哈希表来实现HashMap允许 null 键和 null 值,并且没有顺序保证。HashMap 的操作复杂度为 O(1) 平均复杂度,因此在许多情况下非常高效。 ### 回答2: Java集合Map用来保存键-值对,HashMap是其的一种实现方式。HashMap的内部实现是基于哈希表的,它可以将任意对象作为键,并且保证键的唯一性。在HashMap,键和值都允许为null,但是同一个HashMap,键不能重复,如果重复了,新的value会覆盖旧的value。 HashMap内部是通过hashCode()和equals()方法来判断键是否相等的。当我们向HashMap添加一个键-值对时,系统会先计算出键的hashCode值,然后用该值来确定存放该键值对的位置。如果有多个键的hashCode值相同,称为哈希冲突,那么HashMap会在这些键值对所在的位置上,以链表的形式组成一个链表存储。 HashMap的优点在于插入、删除和查找都比较快,时间复杂度均为O(1),对于大量的数据,它的效率优于List或Set等集合。但是,在内存使用上,HashMap会比List或Set等集合耗费更多的内存空间,因为它需要额外的空间来存储哈希值和链表指针。 值得注意的是,在多线程环境下,HashMap是不安全的,会出现并发修改导致的数据不一致问题,这时可以使用ConcurrentHashMap或者加锁机制来保证线程安全。 总之,HashMapJava非常实用的集合,适用于大量键值对的存储和检索。我们应该了解HashMap的内部实现原理,并且在使用过程需要注意其线程安全性等问题。 ### 回答3: JavaMap是一种键值对的集合,其每个元素都由一个键和一个值组成。在Map,每个键必须是唯一的,而值可以重复。 在MapHashMap是最常用的实现之一。它是基于哈希表实现的,可以通过键快速查找值。哈希表是一种支持常数时间快速查找的数据结构,因为它可以将键映射到与其对应的值的位置,因此查找时只需要计算键的哈希码即可找到对应的值。 HashMap实现使用了两个数组来存储键值对:一个数组用于存储键,另一个数组用于存储值。当插入键值对时,首先会将键的哈希码计算出来,然后通过哈希码将键映射到键数组的位置,将值存储在值数组的相同位置。当需要查找一个键时,只需计算其哈希码并定位到键数组的位置,然后从值数组取出对应的值即可。 与集合一样,HashMap也是线程不安全的,因此在多线程环境下需要使用ConcurrentHashMap或通过synchronized关键字来保证线程安全性。此外,在使用HashMap时应该尽量避免使用null作为键,因为null的哈希码为0,可能与其他非null键的哈希码相同,导致哈希碰撞,影响HashMap的性能。 总之,HashMap是一种高效的键值对集合,通过哈希表实现快速的查找和插入操作。在正确使用和注意安全性的前提下,使用HashMap可以大大提升代码效率和性能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值