文章更新时间:2020/03/03

一、Map介绍 

  Map是Java的一个接口,没有继承,以Key--Value的形式来储存元素信息,常用到的有3个子类实现:

  • HashMap

    • 底层数据结构是散列桶(数组和链表和红黑树)线程不安全【JDK1.8版本】
  • TreeMap
    • 底层数据结构是红黑树线程不安全
  • HashTable
    • 底层数据结构是散列桶(数组和单链表)线程安全

  下面就这3个常用子类进行分析学习。

二、HashMap

  HashMap位于java.util包下

  底层实现散列桶(数组、链表和红黑树)

  特点

  • 数据无序(增删改查访问都很快)
  • 键和值均允许为null
  • 非同步底层由散列表(哈希表)实现
  • 需要设置好初始容量和装载因子才可以发挥最大性能

属性

内部类

底层数据结构说明

主要方法解析

get()

  • 找键的hash地址 :使用对象的hashcode找到bucket(桶)位置
  • 比较键 : 调用keys.equals()方法找到链表中正确的节点,获取值

put()

  • 对Key求Hash值,然后再计算下标
  • 如果没有碰撞,直接放入桶中(碰撞的意思是计算得到的Hash值相同,需要放到同一个bucket中)
  • 如果碰撞了,以链表的方式链接到后面
  • 如果链表长度超过阀值( TREEIFY THRESHOLD==8),就把链表转成红黑树,链表长度低于6,就把红黑树转回链表
  • 如果节点已经存在就替换旧值
  • 如果桶满了(容量16*加载因子0.75),就需要 resize(扩容2倍后重排)

PS即HashMap初始化时的(Node[] table=new Node[16])数组

小结

  • 结构数组内装链表或者红黑树
  • 当线性链表深度大于8时,查询效率会降低,所以1.8以后加入了红黑树,当链表深度超过转换阈值8时,将链表转换为红黑树以提升查询效率
  • 外层数组容量达到0.75时会进行扩容,每次扩容后的容量为原来的2倍
  • 在调用resize()方法时会对原数据进行rehash,此时结构重排,也就出现了红黑树向链表转换的阈值6

三、TreeMap

  TreeMap位于java.util包下

  底层实现红黑树(平衡的二叉搜索树)

  特点

  • 数据有序
  • 比HashMap多实现了了NavigableMap接口,使得键有序
  • 能根据比较器对大小排序
  • 非同步,线程不安全

红黑树的特性

  • (1)树的节点只有两种颜色。
  • (2)色节点的子节点必定是色的。
  • (3)根节点为色的。
  • (4)叶子节点(NIL节点,即空节点)为色的。
  • (5)从任意节点出发,到其后继的叶子节点的路径中,色节点的数目相同。

PS:以上5大特性使得红黑树的根节点到叶子节点的最长路径不会大于最短路径的2倍。

红黑树动态图解可戳这里~

NavigableMap接口

  TreeMap之所以能够实现有序的数据结构,主要原因就是实现了NavigableMap接口,下面借用大佬的一张图来清晰的展示NavigableMap接口的特点:

TreeMap数据结构

  TreeMap储存键值对的单位为一个一个的Entey()对象 ,Entry作为TreeMap存储的结点,包括键、值、父结点、左孩子、右孩子、颜色。源码如下所示:

static final class Entry<K, V> implements Map.Entry<K, V> {
K key;//节点的键
V value;//节点的值
TreeMap.Entry<K, V> left;//左子节点(左孩子)
TreeMap.Entry<K, V> right;//右子节点(右孩子)
TreeMap.Entry<K, V> parent;//父节点
boolean color = BLACK;//节点颜色:默认黑色
}

构造方法

  TreeMap有四种构造函数,,由不同的入参指定:

    //比较器
private final Comparator<? super K> comparator; //1.键自然顺序
public TreeMap() {
comparator = null;
} //2.根据给定的比较器进行排序
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
} //3.构造一个与给定映射具有相同映射关系的新的树映射,该映射根据其键的自然顺序进行排序
public TreeMap(Map<? extends K, ? extends V> m) {
comparator = null;
// putAll()将m中的所有元素添加到TreeMap中
putAll(m);
} //4.构造一个与指定有序映射具有相同映射关系和相同排序顺序的新的树映射
public TreeMap(SortedMap<K, ? extends V> m) {
comparator = m.comparator();
try {
// buildFromSorted将m中的所有元素按同样的排序方式添加到TreeMap中
buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
} catch (Exception e) {
}
}

主要方法解析

put()

  • (1)获取TreeMap的比较器(Comparator)
  • (2)根据比较器规则比较插入的节点,相等时则覆盖,不等时准备下一步工作【没定义比较器时按默认的方式排序】
  • (3)比较树内已存在的节点,并进行节点插入
  • (4)插入节点后检查并修复树【对增节点后的树进行左旋,右旋,变色等操作】,保持平衡

  PS:使用TreeMap时,键必须实现Comparable接口或者构造时传入比较器,若键中存在null值得情况,可以在自定义的比较器中设置null值的处理情况【默认情况下TreeMap是不支持传入null键的】

get()

  • 判断TreeMap存不存在传入的自定应比较器

    • 若存在比较器时,按比较器的规则进行节点查找
    • 若没有自定义比较器时,则按普通二叉排序树的查找规则来搜索树节点

remove()

  • (1)根据key找到待删除的结点
  • (2)删除节点,并修复红黑树

与HashMap的异同

TreeMap和HashMap的相同点

  • 两者均是线程不安全的。
  • 两者插入节点时,key重复均覆盖旧值

TreeMap和HashMap的不同点

  • HashMap的实现基于数组和链表,而TreeMap的实现基于红黑树
  • HashMap的key是无序的,TreeMap的key是有序的。
  • TreeMap要求key必须实现Comparable接口,或者初始化时传入Comparator比较器
  • HashMap增删改查操作的时间复杂度为O(1)TreeMap增删改查操作的时间复杂度为O(log(n))
  • HashMap允许null值TreeMap默认的排序算法中put操作不允许null键,但是值(value)允许为null;若传入自定义比较器,可以手动处理null键的情况。

小结

  • TreeMap的查询、插入、删除效率均没有HashMap高,一般只有要对key排序时才使用TreeMap
  • TreeMap的key不能为null,而HashMap的key可以为null
  • 非线程安全,若须实现同步可:SortedMap m = Collections.synchronizedSortedMap(new TreeMap(...));

四、HashTable

  HashMap位于java.util包下

  底层实现散列桶(数组和单链表)

  特点

  • 数据无序
  • 键和值均不允许为null
  • 线程安全

  具体结构与HashMap相类似,简单归纳如下:

属性

//HashTable的数组【实质是一个Enrty数组,数组的默认长度:11】
private transient Entry<?,?>[] table; //HashTable条目数总量
private transient int count; //下次扩容量【每次扩容大小:2倍旧长度+1】
private int threshold; //负载因子【默认:0.75】
private float loadFactor; //修改次数
private transient int modCount = 0;

主要方法解析

put()

  • (1)判断value不可为空
  • (2)若key已经在table中存在,通过for循环,查找符合条件的key,用新值覆盖旧值,并返回旧值
  • (3)若key不存在则进行新增操作:
    • (modCount)修改次数+1,判断HashTable是否需要扩容
    • 根据hash算法得到索引位置
    • 在指定位置插入新的Entry对象
    • HashTable的(count)条目数 +1 

get()

  • 用与put方法内同样的hash算法计算出key对应的索引下标,去外层的Entry数组内获取对应的value

remove()

  • (1)用与put方法内同样的hash算法计算出key对应的索引下标
  • (2)删除节点,并修复链表

五、总结

HashMap

  • 数据无序
  • 底层实现是散列桶(数组和链表和红黑树)
  • 默认初始化容量是16,每次容量达到外层Entry数组的0.75倍以后就会扩容2倍
  • 键和值均允许为null
  • 非同步,线程不安全

TreeMap

  • 数据有序
  • 比HashMap多实现了了NavigableMap接口,使得键有序
  • 非同步,线程不安全

HashTable

  • 数据无序
  • 键和值均不允许为null
  • 同步,线程安全

  一句话小结:增删改查多用HashMap,需要排序的Map场景用TreeMap,同步场景用HashTable。

参考资料:

Java基础一篇过(五)Map这篇就够了的更多相关文章

  1. Java基础知识常见面试题汇总第一篇

    [Java面试题系列]:Java基础知识常见面试题汇总 第一篇 文中面试题从茫茫网海中精心筛选,如有错误,欢迎指正! 1.前言 ​ 参加过社招的同学都了解,进入一家公司面试开发岗位时,填写完个人信息后 ...

  2. JAVA基础第四章-集合框架Collection篇

    业内经常说的一句话是不要重复造轮子,但是有时候,只有自己造一个轮子了,才会深刻明白什么样的轮子适合山路,什么样的轮子适合平地! 我将会持续更新java基础知识,欢迎关注. 往期章节: JAVA基础第一 ...

  3. 【Java面试题系列】:Java基础知识常见面试题汇总 第一篇

    文中面试题从茫茫网海中精心筛选,如有错误,欢迎指正! 1.前言 ​ 参加过社招的同学都了解,进入一家公司面试开发岗位时,填写完个人信息后,一般都会让先做一份笔试题,然后公司会根据笔试题的回答结果,确定 ...

  4. Java基础复习笔记系列 五 常用类

    Java基础复习笔记系列之 常用类 1.String类介绍. 首先看类所属的包:java.lang.String类. 再看它的构造方法: 2. String s1 = “hello”: String ...

  5. JAVA基础学习day16--集合三-Map、HashMap,TreeMap与常用API

    一.Map简述 1.1.简述 public interface Map<K,V> 类型参数: K - 此映射所维护的键的类型 key V - 映射值的类型 value 该集合提供键--值的 ...

  6. Java基础笔试练习(五)

    1.以下关于Integer与int的区别错误的是? A.int是java提供的8种原始数据类型之一 B.Integer是java为int提供的封装类 C.int的默认值为0 D.Integer的默认值 ...

  7. JAVA基础知识总结:五

    一.初步认识数组 1.理解数组 数组是用来存储相兼容数据类型的定长的容器 特点: a.只能存放相兼容数据类型,不能存放多种数据类型 b.可以存放基本数据类型和引用数据类型 c.数组是定长的,一旦被初始 ...

  8. java基础36 双例集合Map下的HashMap和TreeMap集合

    单例集合体系: ---------| collection  单例集合的根接口--------------| List  如果实现了list接口的集合类,具备的特点:有序,可重复       注:集合 ...

  9. java基础35 双例集合Map及其常用方法

    单例集合体系: ---------| collection  单例集合的根接口--------------| List  如果实现了list接口的集合类,具备的特点:有序,可重复       注:集合 ...

  10. java基础之集合:List Set Map的概述以及使用场景

    本文的整体思路以及部分文字来源:来源一 和 来源二 Java集合类的基本概念: 首先大家要明白集合为什么会出现: 在编程中,常常需要集中存放多个数据.从传统意义上讲,数组是我们的一个很好的选择,前提是 ...

随机推荐

  1. 操作系统-存储管理(5)IA-32/Linux的地址转换

    IA-32/Linux按字节编址:在保护模式下,IA-32采用段页式虚拟存储管理方式,存储地址采用逻辑地址.线性地址和物理地址来进行描述. 逻辑地址由48位组成,包含16位段选择符(高13位为段表项的 ...

  2. day37:MySQL基本操作

    目录 part1:登录mysql的完整语法 part2:查询用户/设置密码/去除密码 part3:给ip/网段/所有ip设置账号密码 part4:查看权限 part5:添加权限/删除权限/删除用户 p ...

  3. MPI计算π

    MPI计算\(\pi\) 利用公式 \[\int_0^1 \frac{4}{1+x^2}dx = \pi \] #include<stdio.h> #include<mpi.h> ...

  4. Charles 断点修改Response

    前言: 我们可以通过map功能进行重定向,但如果同一个域名进行的是不同请求与返回.此时map在这里就不适用了. 我们可以通关对某一请求进行断点,在进行修改请求或者返回.这样就可以满足我们的需求了. 一 ...

  5. SpringCloud微服务项目实战 - API网关Gateway详解实现

    前面讲过zuul的网关实现,那为什么今天又要讲Spring Cloud Gateway呢?原因很简单.就是Spring Cloud已经放弃Netflix Zuul了.现在Spring Cloud中引用 ...

  6. windows快速安装linux虚拟机

    1. 场景描述 因测试中需要linux集群,目前的服务器不太方便部署,需要本机(windows7)启动多个linux虚拟机,记录下,希望能帮到需要的朋友. 2. 解决方案 2.1 软件准备 (1)使用 ...

  7. 深入了解Netty【四】IO模型

    引言 IO模型就是操作数据输入输出的方式,在Linux系统中有5大IO模型:阻塞式IO模型.非阻塞式IO模型.IO复用模型.信号驱动式IO模型.异步IO模型. 因为学习Netty必不可少的要了解IO多 ...

  8. Matlab摄像头视频基本处理

    一.读取摄像头 1.首先保证摄像头及其驱动正确在电脑上安装 2.简单的代码显示驱动摄像头,并显示: vid = videoinput('winvideo',1); preview(vid); 3.默认 ...

  9. 浅析LR.Net工作流引擎

    在当代信息化软件系统开发中,工作流引擎是其中非常重要的一环.所谓工作流引擎,是指工作流作为软件系统的一部分, 其中包括了流程的节点管理.流向管理.流程样例管理.审核管理等重要功能. 工作流引擎可根据角 ...

  10. Java8 Functional(函数式接口)

    Functional 函数式(Functional)接口 只包含一个抽象方法的接口,称为函数式接口. 你可以通过 Lambda 表达式来创建该接口的对象.(若 Lambda 表达式抛出一个受检异常(即 ...