系列文章:

「 深入浅出 」java集合Collection和Map

「 深入浅出 」集合List

「 深入浅出 」集合Set

前面已经介绍完了Collection接口下的集合实现类,今天我们来介绍Map接口下的几个重要的集合实现类HashMap,Hashtable,LinkedHashMap,TreeMap。

HashMap

(最常用,随机访问速度快,无序,可存一个Null key,多个Null value,非同步)

HashMap是最常用的Map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的。因为键对象不可以重复,所以HashMap最多只允许一条记录的键为Null,允许多条记录的值为Null,是非同步的

Hashtable

(HashMap线程安全版,效率低,key和value都不能为null,同步)

Hashtable与HashMap类似,是HashMap的线程安全版,它支持线程同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢,它继承自Dictionary类,不同的是它不允许记录的键或者值为null,同时效率较低。

LinkedHashMap

(有序,遍历性能好,可存一个Null key,多个Null值,非同步)

LinkedHashMap是由散列表+循环双向链表实现的。LinkedHashMap需要维护元素的插入顺序,因此性能略低于HashMap的性能;但是因为它以链表来维护内部顺序,所以在迭代访问Map里的全部元素时有较好的性能。迭代输出LinkedHashMap的元素时,将会按照添加key-value对的顺序输出,有HashMap的全部特性。

TreeMap

(有序,可自定义排序,key不能为空,非同步)

TreeMap 是一个有序的key-value集合,它是通过红黑树实现的,每个key-value对即作为红黑树的一个节点。能够把它保存的记录根据键排序,默认是按键值的升序排序(自然顺序),也可以指定排序的比较器,不允许key值为空,非同步的。

HashMap的实现

下面具体说说hashMap的实现原理(基于jdk1.7)

HashMap的构造函数
//初始容量
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
//默认加载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f; // 默认构造函数。
HashMap() // 指定“容量大小”的构造函数
HashMap(int capacity) // 指定“容量大小”和“加载因子”的构造函数
HashMap(int capacity, float loadFactor) // 包含“子Map”的构造函数
HashMap(Map<? extends K, ? extends V> map)

HashMap有以上4种构造方法,其中HashMap(int capacity)和HashMap(int capacity, float loadFactor)可以指定容量或加载因子。

容量即是HashMap所能存储的"最大"容量(注意:最大加了双引号,因为hashMap会做扩容)

加载因子是HashMap在其容量做扩容前可以达到多满的程度。当容量超出了加载因子与当前容量的乘积时,hashMap会进行扩容达到原来的2倍容量。

当使用默认构造函数HashMap()构建时,会使用默认的容量DEFAULT_INITIAL_CAPACITY = 1 << 4(即是16)和默认加载因子DEFAULT_LOAD_FACTOR = 0.75f

注意:使用HashMap时,尽量指定它的初始容量,因为扩容是按1倍扩展的,如果频繁的扩容会导致性能下降,内存的消耗。

底层存储Entry

上次文中错误是这一块内容,jdk7中低层存储是Entry,jdk8中低层存储是Node

HashMap是通过"拉链法"实现的哈希表。其底层存储结构是Entry数组,Entry实际上就是一个单向链表,哈希表的"key-value键值对"都是存储在Entry中的。

Entry是HashMap的内部类,如下

/**
* 内部类
* hashmap中每一个键值对都是存在Entry对象中,entry还存储了自己的hash值等信息
* Entry被储存在hashmap的内部数组中。
* @param <K> 键值名key
* @param <V> 键值value
*/
static class Entry<K,V> implements Map.Entry<K,V> {
//键对象,final修饰,不可变
final K key;
//值对象
V value;
//下一个键值对Entry对象
Entry<K,V> next;
//键对象的哈希值
int hash; //提供了一个有参构造方法
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
...
}

Java中数据存储方式最底层的两种结构:数组和链表。

数组的特点:连续空间,寻址迅速,但是在刪除或者添加元素的时候需要有较大幅度的移动,所以查询速度快,增刪较慢。

而链表正好相反,由于空间不连续,寻址困难,增刪元素只需修改指針,所以查询速度慢、增刪快。

有沒有一种数组结构來综合一下数组和链表,以便发挥它们各自的优势?答案是肯定的!就是:哈希表(HashMap)。

哈希表具有较快(常量级)的查询速度,及相对较快的增刪速度,所以很适合在海量数据的环境中使用。一般实现哈希表的方法采用“拉链法”,我們可以理解为“链表的数组”,如下图:

从图中,我们可清楚地看出哈希表是由数组+链表组成的,一个长度为16的数组中,每個元素存储的是一个链表的头结点。

通过hash(key)方法判断出元素的存储位置,如果hash(key)值相等,则都存入该hash值所对应的链表中。

这个hash(key)方法是HashMap的一个方法,与Object类的hashCode()是有所区别的,hash(key)在hashCode()方法的基础做一些修改。

HashMap遍历

有3种遍历方式,map.keySet()、map.values()、Map.Entry,例子代码如下

public class Demo2 {
public static void main(String[] args) {
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(1, "aaaa");
map.put(2, "bbbb");
map.put(3, "cccc");
System.out.println(map); // 第一种方式: 使用keySet
// 需要分别获取key和value,没有面向对象的思想
// Set<K> keySet() 返回所有的key对象的Set集合
Set<Integer> ks = map.keySet();
Iterator<Integer> it = ks.iterator();
while (it.hasNext()) {
Integer key = it.next();
String value = map.get(key);
System.out.println("key=" + key + " value=" + value);
} // 第二种方式:map.values()
// 通过values 获取所有值,不能获取到key对象
// Collection<V> values()
Collection<String> vs = map.values();
Iterator<String> it = vs.iterator();
while (it.hasNext()) {
String value = it.next();
System.out.println(" value=" + value);
} // 第三种方式: Map.Entry对象 推荐使用 重点
// Set<Map.Entry<K,V>> entrySet()
// 返回的Map.Entry对象的Set集合 Map.Entry包含了key和value对象
Set<Map.Entry<Integer, String>> es = map.entrySet(); Iterator<Map.Entry<Integer, String>> it = es.iterator(); while (it.hasNext()) { // 返回的是封装了key和value对象的Map.Entry对象
Map.Entry<Integer, String> en = it.next(); // 获取Map.Entry对象中封装的key和value对象
Integer key = en.getKey();
String value = en.getValue(); System.out.println("key=" + key + " value=" + value);
}
}
}

性能分析及适用场景

  • HashMap与Hashtable实现机制几乎一样,但是HashMap比Hashtable性能更好些。

  • LinkedHashMap比HashMap慢一点,因为它需要维护一个双向链表。

  • TreeMap比HashMap与Hashtable慢(尤其在插入、删除key-value时更慢),因为TreeMap底层采用红黑树来管理键值对。

适用场景:

  • 一般的应用场景,尽可能多考虑使用HashMap,因为其为快速查询设计的。

  • 如需特定的排序时,考虑使用TreeMap。

  • 如仅仅需要插入的顺序时,考虑使用LinkedHashMap。

集合篇系列上讲完了,Queue就不讲了,实在是很少用到,请自行学习哈~

推荐阅读:

小黄官宣:排队有序退款

「 第七期福利活动 」15本书籍,无套路直接送!

java小心机(6)| 多态的一些坑

阅读原文,查看更多精彩内容…

您的点赞、转发是对我最大的支持!

THANDKS

  • End -

一个立志成大腿而每天努力奋斗的年轻人

伴学习伴成长,成长之路你并不孤单!

「 深入浅出 」集合Map的更多相关文章

  1. 「 深入浅出 」集合Set

    系列文章 「 深入浅出 」集合List 「 深入浅出 」java集合Collection和Map Set继承自Collection接口,不能包含有重复元素.本篇文章主要讲Set中三个比较重要的实现类: ...

  2. 「 深入浅出 」集合List

    第一篇文章 「 深入浅出 」java集合Collection和Map 主要讲了对集合的整体介绍,本篇文章主要讲List相对于Collection新增的一些重要功能以及其重要子类ArrayList.Li ...

  3. 「BZOJ2839」集合计数

    「BZOJ2839」集合计数 题目大意: 一个包含 \(n\) 个数的集合有 \(2^n\) 个子集,从这些子集中取出若干个集合(至少一个),使他们的交集的元素个数恰好为 \(k\),求方案数,答案对 ...

  4. 「 深入浅出 」java集合Collection和Map

    本系列文章主要对java集合的框架进行一个深入浅出的介绍,使大家对java集合有个深入的理解. 本篇文章主要具体介绍了Collection接口,Map接口以及Collection接口的三个子接口Set ...

  5. 「Foundation」集合

    一.NSArray和NSMutableArray (一)NSArray不可变数组 (1)NSArray的基本介绍 NSArray是OC中使用的数组,是面向对象的,以面向对象的形式操纵对象,是不可变数组 ...

  6. 「Python」Convert map object to numpy array in python 3

    转自Stackoverflow.备忘用. Question In Python 2 I could do the following: import numpy as np f = lambda x: ...

  7. 「UOJ207」共价大爷游长沙

    「UOJ207」共价大爷游长沙 解题思路 : 快速判断两个集合是否完全相等可以随机点权 \(\text{xor}\) 的思路可以用到这道题上面,给每一条路径随机一个点权,维护出经过每一条边的点权的 \ ...

  8. Loj #3042. 「ZJOI2019」麻将

    Loj #3042. 「ZJOI2019」麻将 题目描述 九条可怜是一个热爱打麻将的女孩子.因此她出了一道和麻将相关的题目,希望这题不会让你对麻将的热爱消失殆尽. 今天,可怜想要打麻将,但是她的朋友们 ...

  9. 【LOJ】#3042. 「ZJOI2019」麻将

    LOJ#3042. 「ZJOI2019」麻将 如何判定一个集合牌有没有胡的子集是不是胡的 就用一个\(dp[j][k][0/1]\)表示有j个连续两个的串,有k个连续1个串,有没有对子,再记一下这个集 ...

随机推荐

  1. vue-learning:38 - router - 前端路由的发展

    前端路由的发展 参考博客 前端路由是什么东西? 什么是路由 在jQuery时代,我们使用<a href="https://www.example.com/example/home.ht ...

  2. 如何在很短的时间内将大量数据插入到ConcurrentHashMap(转)

    将大批量数据保存到map中有两个地方的消耗将会是比较大的:第一个是扩容操作,第二个是锁资源的争夺.第一个扩容的问题,主要还是要通过配置合理的容量大小和扩容因子,尽可能减少扩容事件的发生:第二个锁资源的 ...

  3. vue-learning:15 - js - data

    data data属性是一个对象,存放最纯粹的数据,这些数据可以在模板template.计算属性computed.方法methods.监听器watch.过滤器filters.生命周期函数中使用,并且可 ...

  4. html根据下拉框选中的值修改背景颜色

    错误的写法 <!doctype html><html><head><meta charset="utf-8"><title&g ...

  5. GapMinder气泡图:在线互动图表数据平台

    GapMinder:在线互动图表数据平台是一个将国际统计数据转换成活动的.交互的和有趣的图表,以在线统计数据为基础的互动图表集的完美世界.目的是通过增进对可以自由访问的公共统计数据的使用和理解,以促进 ...

  6. Channel 9视频整理【5】

    Jennifer Chiu https://channel9.msdn.com/Niners/JenniferChiu

  7. Python 多组输入

    #基于Python2.7 #若是想Python做到和C++中while(scanf()!=EOF)一样的多组输入效果,可以如实例所示书写 #实例实现了多组输入,计算A+B+C并输出的任务 while ...

  8. webhook功能概述

    1.什么是webhook? webhooks是一个api概念,是微服务api的使用范式之一,也被成为反向api,即:前端不主动发送请求,完全由后端推送. 举个常用例子,比如你的好友发了一条朋友圈,后端 ...

  9. 【转载】CSS flex属性深入理解

    文章转载自 张鑫旭-鑫空间-鑫生活 http://www.zhangxinxu.com/ 原文链接:https://www.zhangxinxu.com/wordpress/2019/12/css-f ...

  10. Nginx 究竟如何处理事件?

    在了解了网络事件以及事件分发收集器以后,让我们来了解 Nginx 是怎么样处理事件的? Nginx 事件循环 当 Nginx 刚刚启动时,在等待事件部分,也就是打开了 80 或 443 端口,这个时候 ...