【JDK源码】TreeMap,2024京东Java面试真题解析

/**

  • 左结点

*/

Entry<K,V> left;

/**

  • 右结点

*/

Entry<K,V> right;

/**

  • 父结点

*/

Entry<K,V> parent;

/**

  • 结点颜色 默认为黑 只有两种颜色,红色和黑色

*/

boolean color = BLACK;

}

对于Map来说,使用的最多的就是put()/get()/remove()等方法,下面依次进行分析

put()

public V put(K key, V value) {

Entry<K,V> t = root;

//如果根结点为null,还没建立

if (t == null) {

//官方给的是:类型(可能为null)检查

compare(key, key); // type (and possibly null) check

//制造一个根结点,默认为黑

root = new Entry<>(key, value, null);

size = 1;

modCount++;

return null;

}

//定义一个cmp,这个变量用来进行二分查找时的比较

int cmp;

Entry<K,V> parent;

// split comparator and comparable paths 拆分比较器和可比较路径

//也就是 cpr表示有无自己定义的排序规则,分两种情况遍历执行,主要目的就是找到要插入结点的父结点

Comparator<? super K> cpr = comparator;

if (cpr != null) {

do {

//存取要插入结点的父结点

parent = t;

//比较新的key与根结点key的大小,相当于维护了二叉排序树

cmp = cpr.compare(key, t.key);

//小的就放左边

if (cmp < 0)

t = t.left;

//大的就放右边

else if (cmp > 0)

t = t.right;

//一样就覆盖,并返回旧的值

else

return t.setValue(value);

} while (t != null);

}

//如果比较器为空,则使用key作为比较器进行比较

else {

//这里要求key不能为空,并且必须实现Comparable接口

if (key == null)

throw new NullPointerException();

@SuppressWarnings(“unchecked”)

//类型转换,也就相当于实现Comparable接口

Comparable<? super K> k = (Comparable<? super K>) key;

do {

parent = t;

cmp = k.compareTo(t.key);

if (cmp < 0)

t = t.left;

else if (cmp > 0)

t = t.right;

else

return t.setValue(value);

} while (t != null);

}

//构建新的结点,其父结点就是上面找到的

Entry<K,V> e = new Entry<>(key, value, parent);

//判断插左边还是右边

if (cmp < 0)

parent.left = e;

else

parent.right = e;

//新插入节点为了保持红黑树平衡,对红黑树进行调整 进行平衡处置,如【变色】【左旋】【右旋】

fixAfterInsertion(e);

size++;

modCount++;

return null;

}

compare

//比较方法,如果comparator==null ,采用comparable.compartTo进行比较,否则采用指定比较器比较大小

final int compare(Object k1, Object k2) {

return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
comparator.compare((K)k1, (K)k2);

}

除去调整红黑树的部分,其余的都很简单,就是搜索二叉树的插入过程

平衡红黑树

fixAfterInsertion

private void fixAfterInsertion(Entry<K,V> x) {

//插入结点的颜色默认是红色

x.color = RED;

//非空,非根结点,父结点为红结点,否则不用操作

while (x != null && x != root && x.parent.color == RED) {

//判断x父结点是否是x爷爷结点的左节结点

if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {

//找到爷爷结点的右结点给到y

Entry<K,V> y = rightOf(parentOf(parentOf(x)));

//如果x的父结点的兄弟结点y是红色,则x的父结点肯定也是红色的

if (colorOf(y) == RED) {

//父结点和叔结节点都为红色,此时通过变色即可实现平衡

//x的父结点设置为黑色

setColor(parentOf(x), BLACK);

//x的父结点的兄弟结点y也设置成黑色

setColor(y, BLACK);

//x的爷爷结点设置为红色

setColor(parentOf(parentOf(x)), RED);

//将x的爷爷结点重置给x

x = parentOf(parentOf(x));

//如果x的父结点和叔父结点是黑色

} else {

//如果x是父结点的右结点

if (x == rightOf(parentOf(x))) {

//将x的父结点重置给x

x = parentOf(x);

//然后左旋

rotateLeft(x);

}

//设置x的父结点为黑色

setColor(parentOf(x), BLACK);

//设置x的爷爷结点为红色

setColor(parentOf(parentOf(x)), RED);

//将x的爷爷结点右旋

rotateRight(parentOf(parentOf(x)));

}

//x父结点是x爷爷结点的右节结点

} else {

//找到爷爷结点的左结点给到y

Entry<K,V> y = leftOf(parentOf(parentOf(x)));

//如果父结点和叔结节点都为红色,此时通过变色即可实现平衡 和上面一样

if (colorOf(y) == RED) {

setColor(parentOf(x), BLACK);

setColor(y, BLACK);

setColor(parentOf(parentOf(x)), RED);

x = parentOf(parentOf(x));

//都为黑

} else {

//如果x是父结点的左结点

if (x == leftOf(parentOf(x))) {

//将父结点重置给x

x = parentOf(x);

//右旋x

rotateRight(x);

}

//设置x的父结点为黑色

setColor(parentOf(x), BLACK);

//设置x的爷爷结点为红色

setColor(parentOf(parentOf(x)), RED);

//左旋爷爷结点

rotateLeft(parentOf(parentOf(x)));

}

}

}

//根结点一定是黑色

root.color = BLACK;

}

  • 设置颜色左右旋的目的就是保证红黑树的规则

红黑树是一个更高效的检索二叉树,有如下特点:

  1. 每个节点只能是红色或者黑色

  2. 根节点永远是黑色的

  3. 每个叶子节点(NIL)是黑色。(注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!)

  4. 如果一个节点是红色的,则它的子节点必须是黑色的。

  5. 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点

左旋

在这里插入图片描述

  • 其实就是 P结点为树的结构改变,将P结点的右结点R作为父结点,将R的左结点给到P结点的右结点,将P结点给到R结点的左结点,重新变成以R结点为树节点的二叉树

/** From CLR */

/**

  • 以p为树 左旋

*/

private void rotateLeft(Entry<K,V> p) {

if (p != null) {

//取出P的右结点给r

Entry<K,V> r = p.right;

//将r的左结点给p的右结点,其实就是p的右结点指向p的右结点的左结点

p.right = r.left;

//不为空则反过来指向

if (r.left != null)

r.left.parent = p;

//将r的父结点指向p的父结点

r.parent = p.parent;

//如果p本来就是整个树的根结点,则将r作为根结点

if (p.parent == null)

root = r;

//将r作为此树的根结点

else if (p.parent.left == p)

//原来p是父结点的左结点 就将r作为p父结点的左结点

p.parent.left = r;

else

p.parent.right = r;

//再将r左结点指向p

r.left = p;

p.parent = r;

}

}

右旋

在这里插入图片描述

  • 其实就是 P结点为树的结构改变,将P结点的左结点R作为父结点,将L的右结点给到P结点的左结点,将P结点给到L结点的右结点,重新变成以L结点为树节点的二叉树

/** From CLR */

/**

  • 以p为树 右旋 原理和左旋一样

*/

private void rotateRight(Entry<K,V> p) {

if (p != null) {

Entry<K,V> l = p.left;

p.left = l.right;

if (l.right != null) l.right.parent = p;

l.parent = p.parent;

if (p.parent == null)

root = l;

else if (p.parent.right == p)

p.parent.right = l;

else p.parent.left = l;

l.right = p;

p.parent = l;

}

}

get()

public V get(Object key) {

Entry<K,V> p = getEntry(key);

return (p==null ? null : p.value);

}

final Entry<K,V> getEntry(Object key) {

// Offload comparator-based version for sake of performance

//如果comparator不为空,使用comparator的版本获取元素

if (comparator != null)

//代码和根节点遍历一样

return getEntryUsingComparator(key);

if (key == null)

throw new NullPointerException();

@SuppressWarnings(“unchecked”)

//类型转换 就是去实现Comparable

Comparable<? super K> k = (Comparable<? super K>) key;

//从根节点开始遍历

Entry<K,V> p = root;

while (p != null) {

int cmp = k.compareTo(p.key);

//小就从左边找 因为在put的时候满足二叉排序树

if (cmp < 0)

p = p.left;

//大就从右边找

else if (cmp > 0)

p = p.right;

else

return p;

}

return null;

}

remove()

public V remove(Object key) {

Entry<K,V> p = getEntry(key);

if (p == null)

return null;

V oldValue = p.value;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

最后

毕竟工作也这么久了 ,除了途虎一轮,也七七八八面试了不少大厂,像阿里、饿了么、美团、滴滴这些面试过程就不一一写在这篇文章上了。我会整理一份详细的面试过程及大家想知道的一些问题细节

美团面试经验

美团面试
字节面试经验
字节面试
菜鸟面试经验
菜鸟面试
蚂蚁金服面试经验
蚂蚁金服
唯品会面试经验
唯品会

因篇幅有限,图文无法详细发出
…(img-vDuxlnML-1712036796320)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-MEye9bJD-1712036796321)]

最后

毕竟工作也这么久了 ,除了途虎一轮,也七七八八面试了不少大厂,像阿里、饿了么、美团、滴滴这些面试过程就不一一写在这篇文章上了。我会整理一份详细的面试过程及大家想知道的一些问题细节

美团面试经验

[外链图片转存中…(img-6hWMPiHj-1712036796321)]
字节面试经验
[外链图片转存中…(img-k8TbQTRV-1712036796321)]
菜鸟面试经验
[外链图片转存中…(img-MtFrpcO6-1712036796322)]
蚂蚁金服面试经验
[外链图片转存中…(img-HRHVEzz8-1712036796322)]
唯品会面试经验
[外链图片转存中…(img-1Gm6FrOd-1712036796322)]

因篇幅有限,图文无法详细发出

  • 14
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值