HashMap详解

HashMap是一种键值对存储的数据结构,基于哈希表实现,线程不安全。在Java7中,HashMap内部是数组+链表,当链表达到一定长度时会转为红黑树,以提高查找效率。Java8引入了更多的红黑树操作,进一步优化了性能。红黑树是一种自平衡二叉查找树,具有特定的颜色规则以保持平衡。文章详细介绍了HashMap的实现原理、Java7与Java8的区别以及红黑树的左旋、右旋、添加和删除操作。
摘要由CSDN通过智能技术生成

HashMap(数组+链表+红黑树)

特征:键不可重复,值可重复,底层Hash表,线程不安全,允许key为null,值为null。

  • HashMap根据键的hashcode值存储数据,大多数情况下可以直接定位到它的值,因而具有很快的访问速度,但遍历顺序却是不确定的。
  • HashMap最多允许一条键的值为null,允许多条记录为null。
  • HashMap非线程安全,即任一时刻可以有多个线程同时写HashMap,可能导致数据不统一,如果需要满足线程安全可以用Collection的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap

HashMap(java7实现)

在这里插入图片描述
大体上HashMap里面是一个数组,然后数组中每个元素是一个单向链表,上图中绿色的实体是嵌套类Entry的实例,Entry包含四个属性:key,value,hash值和用于单向链表的next

  1. capacity:当前数组容量,始终保持2^n,可以扩容,扩容后数组大小为当前的2倍
  2. loadFactor:负载因子,默认为0.75
  3. threshold:扩容的阈值,等于capacity*loadFactor

HashMap(java8实现)

java8最大的不同是运用了红黑树,根据java7HashMap,查找的时候根据hash值能够快速确定数组的具体下标,但是之后需要顺着链表一个个比较下去才能找到我们需要的,时间复杂度取决于链表的长度为O(n),为了降低这部分的开销,在java8中,当链表中的元素超过8个之后会将链表转换为红黑树,在这些位置进行查找的时候可以降低时间复杂度为O(logN)
在这里插入图片描述

红黑树实现

R-B Tree 全称是Red-Black Tree,又称红黑树,一种特殊的二叉查找树,红黑树的每个节点都有存储位表示节点的颜色,可以是红色或黑色

  1. 红黑树的特性
    (1)每个节点是红色或黑色
    (2)根节点是黑色
    (3)每个叶子节点(NIL)是黑色[注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点]
    (4)如果一个节点是红色的,则它的子节点必须是黑色的
    (5)从一个节点到该节点的子孙节点的所有路径上包含相同的黑节点

  2. 左旋
    对x进行左旋,意味着将‘x’的右孩子设为x的父亲节点,即将x变成一个左节点(x成了z的左孩子),因此左旋的左意味着被旋转的节点将变成一个左节点
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  3. 右旋
    对x进行右旋,意味着将x的左孩子设为x的父亲节点,即将x变成一个右节点(x成y的右孩子)因此,右旋中的右意味着被旋转的节点变成一个右节点
    在这里插入图片描述在这里插入图片描述

  4. 添加
    第一步:将红黑树当做一颗二叉查找树,将节点插入。
    第二步:将插入的节点着色为红色
    根据被插入的节点的父节点情况,可以将“当节点Z被着色成红色节点,并插入二叉树”划分为三种情况来处理
    (1)被插入的节点是根节点
    处理方法:直接把此节点涂为黑色
    (2)被插入的节点的父节点是黑色
    处理方法:什么也不做,节点被插入后,仍然是红黑树
    (3)被插入的节点的父节点是红色,这种情况下,被插入节点是一定存在非空祖父节点的,进一步讲,被插入的节点也一定存在叔叔节点(即使叔叔节点为空,我们也当他存在,空节点本身就是黑节点),依据叔叔节点的情况,将这种情况进一步划分为3种情况


第三步:通过一系列的旋转或着色操作,使之重新成为一颗红黑树

  1. 删除
    第一步:将红黑树当做一个二叉查找树,将节点删除,和删除二叉查找树一样的方法,分为三种情况
    (1)被删除节点没有儿子,即为叶子结点,直接删除就好了
    (2)被删除节点只有一个儿子,直接删除节点,并用该节点的唯一子节点顶替它的位置
    (3)被删除节点有两个儿子,先找出后继节点,然后将它的后继节点的内容复制给该节点的内容,然后将后继节点删除
    第二步:通过旋转和重新着色,使之重新成为红黑树
    选择重着色3种情况
    (1)x是红+黑节点
    处理方法:直接把x设为黑色,
    (2)x是黑+黑节点,且x是根
    处理方法:什么都不做
    (3)x是黑+黑节点,且x不是根
    处理方法:这种情况又分为4种子情况,如下图所示
    在这里插入图片描述

代码实现

  1. 基本定义
public class RBTree<T extends Comparable<T>>{
   
	private RBTNode<T> mRoot;	//根节点
	private static final boolean RED = false;
	private static final boolean BLACK = true;
	public class RBTNode<T extends Comparable<T>>{
   
		boolean color;	//颜色
		T key;	//关键字
		RBTNode<T> left;	//左孩子
		RBTNode<T> right;	//右孩子
		RBTNode<T> parent;	//父节点
		
		public RBTNode(T key, boolean color, RBTNode<T> parent, RBTNode<T> left, RBTNode<T> right) {
   
            this.key = key;
            this.color = color;
            this.parent = parent;
            this.left = left;
            this.right = right;
        }
	}
}
  1. 左旋
private void leftRotate(RBTNode<T> x){
   
	//设置x的右孩子为y
	RBTNode<T> y = x.right;
	//将y的左孩子设为x的右孩子
	//如果y的左孩子为空,将x设为y的左孩子的父亲
	x.right=y.left;
	if(y.left!=null)
		y.left.parent = x;
	//将x的父亲设为y的父亲
	y.parent = x.parent;
	if(x.parent==null){
   
		this.mRoot=y;	//如果x的父节点是空节点,则将y设为根节点
	}else{
   
		if(x.parent.left==x)
			x.parent.left = y; //如果x是它父节点的左孩子,则将y设为x的父节点的左孩子
		else
			x.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值