女朋友问我HashTable内部结构是什么、扩容什么时候扩容的?

// 负载因子 = 负载系数

private float loadFactor;

//修改的次数

private transient int modCount = 0;

复制代码

构造器


/**

主要还是调用了这个构造

initialCapacity : 数据容量

loadFactor : 负载因子

*/

public Hashtable(int initialCapacity, float loadFactor) {

if (initialCapacity < 0) // 判断初始的容量是否小于0

throw new IllegalArgumentException("Illegal Capacity: "+

initialCapacity);

if (loadFactor <= 0 || Float.isNaN(loadFactor))// 判断负载因子是否小于等于0和传递的参数值是否合法

throw new IllegalArgumentException("Illegal Load: "+loadFactor);

if (initialCapacity==0) // 数据容量是否等等于0

initialCapacity = 1; // 如果是赋值为1

this.loadFactor = loadFactor; // 负载因子

table = new Entry<?,?>[initialCapacity]; // 初始数组table的容量大小

// threshold = 容量大小 * (负载系数 = 负载因子)

threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);

}

// 自定义数组容量的构造

public Hashtable(int initialCapacity) {

this(initialCapacity, 0.75f);

}

// 无参构造

public Hashtable() {

this(11, 0.75f); // 它调用了一个有参构造

}

// 将另一个map集合的数据放入到Hashtable集合中

public Hashtable(Map<? extends K, ? extends V> t) {

this(Math.max(2*t.size(), 11), 0.75f);

putAll(t); // 这个方法做的事情就是遍历t集合的数据、调用Hashtable的put方法进行添加数据

}

复制代码

对集合CRUD简单说明


public synchronized V put(K key, V value) {

// Make sure the value is not null

if (value == null) { // value 不能为null 否者抛出异常

throw new NullPointerException();

}

// 将构造中已经初始化的赋值给tab

Entry<?,?> tab[] = table;

// 计算hash值

int hash = key.hashCode();

// 计算下标

int index = (hash & 0x7FFFFFFF) % tab.length;

@SuppressWarnings(“unchecked”)

// 找到该下标中的节点链表

Entry<K,V> entry = (Entry<K,V>)tab[index];

// 遍历该链表下的节点

for(; entry != null ; entry = entry.next) {

// 判断hash值是否相同等、内容是否相等

if ((entry.hash == hash) && entry.key.equals(key)) {

V old = entry.value; // 将旧的value值赋值给old

entry.value = value; // 将新的value值赋值给旧的value值

return old; // 将旧的value值返回

}

}

// 如果链表没有重复的则进行添加在其他位置上

addEntry(hash, key, value, index); // 后面来说这个方法

return null;

}

复制代码

问题:

获取Hash值 :int hash = key.hashCode();

  1. 通过执行key对应类型实现的hashCode()方法获取对应的Hash值、没有实现则调用object类中的hashCode()来获取hash值、但是不能为null值会报空指针异常

获取下标:int index = (hash & 0x7FFFFFFF) % tab.length;

  1. 通过hash去取得一个下标0x7FFFFFFF表示int类型最大的数2147483647、hash值按位与0x7FFFFFFF的结果取余tab数组的长度。

public synchronized V remove(Object key) {

Entry<?,?> tab[] = table; // 得到元素数组

int hash = key.hashCode(); // 算出Hash值

int index = (hash & 0x7FFFFFFF) % tab.length; // 算出下表

@SuppressWarnings(“unchecked”) // 抑制警告

// 获取改下标中的链表

Entry<K,V> e = (Entry<K,V>)tab[index];

for(Entry<K,V> prev = null ; e != null ; prev = e, e = e.next) {

// 如果hash只相同、并且内容相同

if ((e.hash == hash) && e.key.equals(key)) {

modCount++;// 操作数++

if (prev != null) { // 如果遍历到链表有上一个节点的情况下成立

prev.next = e.next;

} else {

tab[index] = e.next; // 将下一个节点的数据给数组

}

count–; // 内容数量–

V oldValue = e.value; // 将下一个节点赋值、作为返回出去

e.value = null; //将null赋值给下一个节点

return oldValue; // 返回被移除的元素

}

}

return null; // 没有则返回null

}

复制代码

改和增加是一样的他也是、通过key的hash值来找到对应位置、将其对应值覆盖

public synchronized V get(Object key) {

Entry<?,?> tab[] = table; // 得到元素数组

int hash = key.hashCode(); // 算出Hash值

int index = (hash & 0x7FFFFFFF) % tab.length; // 算出下表

// 遍历链表

for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {

if ((e.hash == hash) && e.key.equals(key)) { // hash 相同值相同成立

return (V)e.value; // 得到key对应的值、将其返回出去

}

}

return null; // 没有就返回null

}

复制代码

HashTable的核心内容扩容机制


扩容是在元素添加时、判断数组的大小是否能够存入下一个值、而做的操作扩容

----------------- 添加方法 ------------------

public synchronized V put(K key, V value) {

// Make sure the value is not null

if (value == null) { // value 不能为null 否者抛出异常

throw new NullPointerException();

}

// 将构造中已经初始化的赋值给tab

Entry<?,?> tab[] = table;

// 计算hash值

int hash = key.hashCode();

// 计算下标

int index = (hash & 0x7FFFFFFF) % tab.length;

@SuppressWarnings(“unchecked”)

// 找到该下标中的节点链表

Entry<K,V> entry = (Entry<K,V>)tab[index];

// 遍历该链表下的节点

for(; entry != null ; entry = entry.next) {

// 判断hash值是否相同等、内容是否相等

if ((entry.hash == hash) && entry.key.equals(key)) {

V old = entry.value; // 将旧的value值赋值给old

entry.value = value; // 将新的value值赋值给旧的value值

return old; // 将旧的value值返回

}

}

// 如果链表没有重复的则进行添加在其他位置上

addEntry(hash, key, value, index); // 后面来说这个方法

return null;

}

---------------addEntry()-----------------

private void addEntry(int hash, K key, V value, int index) {

modCount++; // 修改次数++

Entry<?,?> tab[] = table;

// 元素数量大于等于阈值进行扩容操作threshold 默认为8

if (count >= threshold) {

// Rehash the table if the threshold is exceeded

rehash(); // 扩容主要方法

tab = table;

hash = key.hashCode();

index = (hash & 0x7FFFFFFF) % tab.length; // 扩容后重新计算索引下标

}

// Creates the new entry.

@SuppressWarnings(“unchecked”)

// 第一次这个节点为null

Entry<K,V> e = (Entry<K,V>) tab[index];

// 创建一个新的节点、赋值给tab[求出下标索引的位置上]

tab[index] = new Entry<>(hash, key, value, e);

count++; // 长度++

}

---------------rehash()-----------------

protected void rehash() {

int oldCapacity = table.length; // 先获取数组的长度

Entry<?,?>[] oldMap = table; // 获取数据

// overflow-conscious code

int newCapacity = (oldCapacity << 1) + 1; // 扩容成2倍+1

// 判断新扩容的容量大小是否超过默认的最大值

if (newCapacity - MAX_ARRAY_SIZE > 0) {

if (oldCapacity == MAX_ARRAY_SIZE)

// Keep running with MAX_ARRAY_SIZE buckets

return;

// 如果超过将最大值赋值给新容量

newCapacity = MAX_ARRAY_SIZE;

}

// newCapacity 获取到新容量的大小后创建一个最新容量大小的数组

Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];

modCount++; // 修改次数++

// 计算新的threshold(阈值) 、 超过就进行扩容操作

threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);

table = newMap; // 重新给数组赋值

// 数据迁移遍历数据、将原来数组中的数据放到新数组中

for (int i = oldCapacity ; i-- > 0 😉 {

for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {

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

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

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

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

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

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

Kafka实战笔记

关于这份笔记,为了不影响大家的阅读体验,我只能在文章中展示部分的章节内容和核心截图

image.png

  • Kafka入门
  • 为什么选择Kafka
  • Karka的安装、管理和配置

image.png

  • Kafka的集群
  • 第一个Kafka程序
  • image.png

afka的生产者

image.png

  • Kafka的消费者
  • 深入理解Kafka
  • 可靠的数据传递

image.png

image.png

  • Spring和Kalka的整合
  • Sprinboot和Kafka的整合
  • Kafka实战之削峰填谷
  • 数据管道和流式处理(了解即可)

image.png

  • Kafka实战之削峰填谷

image.png

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
(img-dhiCo17n-1713631369832)]

  • Kafka的消费者
  • 深入理解Kafka
  • 可靠的数据传递

[外链图片转存中…(img-9fp60VMa-1713631369832)]

[外链图片转存中…(img-RTKVsuMa-1713631369832)]

  • Spring和Kalka的整合
  • Sprinboot和Kafka的整合
  • Kafka实战之削峰填谷
  • 数据管道和流式处理(了解即可)

[外链图片转存中…(img-HJoNTsxp-1713631369833)]

  • Kafka实战之削峰填谷

[外链图片转存中…(img-0slkTPs6-1713631369833)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值