Java - 提高-源码(10) - HashMap

深度剖析Java HashMap:源码分析、线程安全与最佳实践 Java中的HashMap是最常用的数据结构之一,在实际开发中起着至关重要的作用。本文将详细探讨HashMap的工作原理、源码分析、线程安全问题、以及扩容机制等方面。 阅读详情

源码下载地址:jdk1.7源码下载


HashMap的实现

源码分析对应JDK1.7

参考资料:
http://www.codeceo.com/article/java-hashmap-from-code-layer.html#0-tsina-1-24433-397232819ff9a47a7b7e80a40613cfe1
http://www.cnblogs.com/chenssy/p/3521565.html


先记住结论
HahsMap  继承AbstractMap;
HashMap  的key和value都是可以为null;
HashMap  是无序的;
HashMap  不是同步的,如果需要线程安全的HahsMap,可以通过Collections类的静态方法synchronizedMap获得
      线程安全的HashMap。


HashMap 中有两个重要参数:"初始容量","加载因子"
初始容量:hashmap底层table数组(entry)的长度
加载因子:算是一种阀值,当table数组中有效数据条目超出 加载因子与当前容量(table数组总长度)的乘积时,
则对哈希表进行扩容。
(有效数据条目意思:数组长度16,里面存了10条数据,有效条目就是10条数据,不是数组总长)


HashMap的数据结构
说到数据结构,想到这个图,画的很好,直接拿来用,感谢作者


之前在看上面这张HashMap结构图的时候,一直很好奇什么情况下链表会出现多个?
后来偶然测试发现一个例子:
这里我将hashMap源码中的代码拿出来测试

HashMap<String, Integer> map = new HashMap<String, Integer>();
 map.put("语文", 1);
 map.put("数学", 2);
 map.put("英语", 3);
 map.put("历史", 4);
 map.put("政治", 5);
 map.put("地理", 6);
 map.put("生物", 7);
 map.put("化学", 8);
 map.put("化学", 9);

在put的时候,"历史" 和 "语文" 就出现了entry链表
然后我将hashMap put时的源码拿出来测试了一下。

public static void main(String[] args) {

		int str = "语文".hashCode();
		int str2 = "历史".hashCode();

		System.out.println("hashCode : " + str);
		System.out.println("hashCode : " + str2);

		System.out.println("table 数组下标 : " + hash(str) % 16);
		System.out.println("table 数组下标 : " + (hash(str2) % 16));
		}

		/**
		 * 根据key的hash计算出table数组的下标.
		 * 
		 * @param h
		 * @return
		 */
		static int hash(int h) {
				// This function ensures that hashCodes that differ only by constant
				// multiples at each bit position have a bounded number of collisions
				// (approximately 8 at default load factor).
				h ^= (h >>> 20) ^ (h >>> 12);
				return h ^ (h >>> 7) ^ (h >>> 4);
		}
		
输出结果:
hashCode : 1136442
hashCode : 684332
table 数组下标 : 0
table 数组下标 : 0
你看,通过hashMap中的hash算法之后,计算出他们两个的下标是一样的,然后就出现了entry链表。



HashMap中 Entry的源码:

static class Entry<K, V> implements Map.Entry<K, V> {
	final K key;
	V value;
	// 当put发生碰撞时,指向下一个节点
	Entry<K, V> next;
	int hash;

	/**
	 * Creates new entry.<br>
	 * 构造函数<br>
	 * 
	 */
	Entry(int h, K k, V v, Entry<K, V> n) {
			value = v;
			next = n;// 下一个节点(entry链表)
			key = k;
			hash = h;
	}

	public final K getKey() {
			return key;
	}

	public final V getValue() {
			return value;
	}

	public final V setValue(V newValue) {
			V oldValue = value;
			value = newValue;
			return oldValue;
	}

	/**
	 * entry中的equlas方法,判断两个entry是否相等<br>
	 * 若两个Entry的key和value都相等,则返回true<br>
	 * 否则返回false
	 * 
	 */
	public final boolean equals(Object o) {
			if (!(o instanceof Map.Entry))
					return false;
			Map.Entry e = (Map.Entry) o;
			Object k1 = getKey();
			Object k2 = e.getKey();
			if (k1 == k2 || (k1 != null && k1.equals(k2))) {
					Object v1 = getValue();
					Object v2 = e.getValue();
					if (v1 == v2 || (v1 != null && v1.equals(v2)))
							return true;
			}
			return false;
	}

	// entry中的hashCode方法
	public final int hashCode() {
			return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode());
	}

	public final String toString() {
			return getKey() + "=" + getValue();
	}

	/**
	 * This method is invoked whenever the value in an entry is overwritten
	 * by an invocation of put(k,v) for a key k that's already in the
	 * HashMap.
	 */
	void recordAccess(HashMap<K, V> m) {
	}

	/**
	 * This method is invoked whenever the entry is removed from the table.
	 */
	void recordRemoval(HashMap<K, V> m) {
	}
	}
通过上面那个图,可以看出,HashMap底层是一个数组,数组中存放的是Entry<K,V>;
通过entry的源码,发现每个Entry<K,V>的next,维护了entry链表

HashMap的构造方法

HashMap():
构造一个默认初始容量为16,默认加载因子为0.75的空HahsMap
HashMap(int initialCapacity):
构造一个  带指定初始容量 ,默认加载因子为0.75的空HashMap
HashMap(int initialCapacity, float loadFactor):
构造一个  带指定初始容量 和 加载因子的空HashMap
HashMap(Map<? extends K, ? extends V> m):
构造一个 映射关系与指定Map相同的新HashMap


下面看个通用的构造方法:
HashMap(int initialCapacity) 和 HashMap() 构造函数最后都是指向这个构造方法。

/**
* 指定容量大小和加载因子的构造函数
* 
* @param initialCapacity
* @param loadFactor
*/
public HashMap(int initialCapacity, float loadFactor) {

	// 参数校验
	if (initialCapacity < 0)
			throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);

	// 指定容量是否超出最大容量限制校验,如果超出,就用最大容量
	if (initialCapacity > MAXIMUM_CAPACITY)
			initialCapacity = MAXIMUM_CAPACITY;

	// 校验加载因子,判断参数是否是"非数字"的值
	if (loadFactor <= 0 || Float.isNaN(loadFactor))
			throw new IllegalArgumentException("Illegal load factor: " + loadFactor);

	// Find a power of 2 >= initialCapacity
	// 找出"大于initialCapacity(指定容量)的最小2的幂"
	int capacity = 1;
	while (capacity < initialCapacity)
			capacity <<= 1;

	// 加载因子赋值
	this.loadFactor = loadFactor;

	// 设置HashMap的阀值,当HashMap中存储的数据量达到threshold时,需要扩容
	// Math.min():选择一个小的参数
	threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);

	// 创建指定长度的table数组(entry)
	table = new Entry[capacity];

	useAltHashing = sun.misc.VM.isBooted() && (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
	init();
}
这里可能有个疑问,或者面试的时候回问道:
为什么扩容一定是2的幂次?

这里直接说结论:

当HahsMap的容量是2的幂次时,不同的hash值发生碰撞的概率比较小,这样数据在table数组中分布的均匀,查询速度也快。


分析:

HashMap的底层数组长度总是2的幂次,在构造方法中有这样几行代码

int capacity = 1;
while (capacity < initialCapacity)
	capacity <<= 1;
这段代码的作用是:找出"大于initialCapacity(指定容量)的最小2的幂"。
当length为2的幂次时indexFor()方法
static int indexFor(int h, int length) {
	return h & (length - 1);
}
就相当于对length取摸,而且速度比直接取模快的多,这是HashMap在速度上的一个优化。

indexFor方法,该方法仅有一条语句:h & (length - 1),
这段代码除了上面说的取模运算外还有一个非常重要的责任:均匀分布table数据和充分利用空间。

这里我们假设length为16 和 15,其中16为2的幂次,h为5,6,7


当length为15时,h的6,7的结果是一样的,这就表示它们在table数组中的存储位置是相同的,也就是产生了碰撞。
6,7就会在一个位置上形成链表,这样子就会导致查询速度降低。

我们扩大h的值,从0 ~ 15,length为15

从上面这个图中发现,一共发生了8次碰撞,同时发现浪费的空间非常大,
1,3,5,7,9,11,13,15处没有记录,也就是没有存放数据。

而当length=16时,length-1=15,即1111,那么进行低位&运算时,值总是与原来得hash值相同,而进行高位运算时,
其值等于其低位值。(0~14 与 0~15 ,与table数组长度比较)

所以说当length=2^n时,不同的hash值发生碰撞的概率比较小,这样会使得数据在table数组中分布均匀,查询速度也快



put(K,V)方法

public V put(K key, V value) {

	// 如果key为空,将null存放在table[0]第一个位置,这就是HashMap允许存null的原因
	if (key == null)
			return putForNullKey(value);

	// 计算key的hash值
	int hash = hash(key);    -----------------(1)

	// 根据hash码和数组长度,计算table数组下标
	int i = indexFor(hash, table.length);  ---------------(2)

	// 从i处开始迭代entry链表,找到key保存的位置
	for (Entry<K, V> e = table[i]; e != null; e = e.next) {
			Object k;
			// 判断该链条上是否有hash值相同的(key相同)
			// 若存在key相同,直接覆盖value,返回旧的value
			if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
					V oldValue = e.value;// 取出旧值
					e.value = value;// 赋新值
					e.recordAccess(this);
					return oldValue;// 返回旧值
			}
	}

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

	// i处没有entry链表(该位置为空),将key,value添加至i处
	addEntry(hash, key, value, i);

	return null;
}
a. 判断key是否为null,为null直接调用putForNullKey()方法处理
b. key不为null,计算key的hash值
c. 根据hash值,计算table数组中的下标位置
d. 如果该下标位置有entry,则比较是否是相同的key,如果是相同的,覆盖value
key不同则将新的key/value存入entry链表表头,旧的往后移。
e. 如果下标处没有entry,则直接存储


在上面代码的for循环处,此处的迭代为了防止存在相同的key值,如果两个hash值(key)相同,
HashMap的处理方式是用新的value替换旧的value,并没有处理key,这就解释了HashMap中没有两个相同的key,
(以及后面的HashSet,key是唯一的,这个后面再说)


在(1),(2)处,这里是比较关键的地方:
hash()方法

final int hash(Object k) {
	int h = 0;
	if (useAltHashing) {
			if (k instanceof String) {
					return sun.misc.Hashing.stringHash32((String) k);
			}
			h = hashSeed;
	}

	h ^= k.hashCode();

	// This function ensures that hashCodes that differ only by
	// constant multiples at each bit position have a bounded
	// number of collisions (approximately 8 at default load factor).
	h ^= (h >>> 20) ^ (h >>> 12);
	return h ^ (h >>> 7) ^ (h >>> 4);
}
首先获得k的hashCode,然后对hashCode值进行计算(纯数学计算,各种移位计算)

indexFor()方法

static int indexFor(int h, int length) {
		return h & (length - 1);
}
对于HashMap的table[]而言,数据分布需要均匀(最好每个下标只有一个元素,直接取值),
不能太紧也不能太松,太紧会导致查询速度慢,太松则浪费空间。
indexFor()方法的责任就是:均匀的分布table[]中的数据和充分利用空间

putForNullKey()方法,这个方法处理key为null的情况

/**
* 该方法的作用是将"key为null"的键值对,存放到table[0]位置
*/
private V putForNullKey(V value) {

	// 直接遍历table[0]位置的Entry链表
	for (Entry<K, V> e = table[0]; e != null; e = e.next) {

	// 寻找链表中key为null的Entry
	if (e.key == null) {
			V oldValue = e.value;// 取出旧value值
			e.value = value;// 赋值新的value
			e.recordAccess(this);
			return oldValue;// 返回旧value
	}
	}

	// 如果没有找到key为null的entry,说明table[0]位置没有Entry
	// HashMap被改变数计数器+1
	modCount++;

	// 将key为null的键值对添加入到Entry中
	addEntry(0, null, value, 0);

	return null;
}

最终将key,value插入Entry的两个方法

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

	// 首先判断是否需要扩容
	// 'hashMap的大小' 大于等于 '阀值(加载因子*容量)' && table数组对应下标位置有数据
	if ((size >= threshold) && (null != table[bucketIndex])) {
			// 容量扩大两倍
			resize(2 * table.length);

	// key为null,hash取0
	// key不为null,根据key计算hash
	hash = (null != key) ? hash(key) : 0;

	// 重新计算哈希码的索引
	bucketIndex = indexFor(hash, table.length);
	}

	// 创建entry
	createEntry(hash, key, value, bucketIndex);
}

/**
* 
* @param hash
*            hash值
* @param key
* @param value
* @param bucketIndex
*            table数组下标
*/
void createEntry(int hash, K key, V value, int bucketIndex) {
	Entry<K, V> e = table[bucketIndex];
	table[bucketIndex] = new Entry<>(hash, key, value, e);
	size++;// hashMap大小+1
}

有两点需要注意:
a. 链表的产生
HahsMap总是将新的Entry对象添加到bucketIndex处。
如果bucketIndex处已经有了对象,那么新添加的Entry将指向原有的Entry对象,形成一条Entry链。
但如果bucketIndex处没有Entry对象,直接将数据塞进去,不会形成链表

b. 扩容问题
随着HahsMap中元素得数量越来越多,发生碰撞的概率越来越大,所以产生的链表会越来越长,
为了保证HashMap的速度以及效率,系统必须进行扩容处理,而扩容处理非常耗时,所以如果能
预知HashMap中元素的数量,在构造的时候,直接设置。


get(Object key)方法

get方法源码

public V get(Object key) {

	// 如果key为null,直接调用getForNullKey()方法
	if (key == null)
			return getForNullKey();

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

	// 返回value
	return null == entry ? null : entry.getValue();
}

private V getForNullKey() {
	// 默认重table[]数组第一位取Entry
	for (Entry<K, V> e = table[0]; e != null; e = e.next) {
			if (e.key == null)
					return e.value;// 返回value
	}
	return null;
}

final Entry<K, V> getEntry(Object key) {
	// 计算key的hash值
	int hash = (key == null) ? 0 : hash(key);

	// 根据hash值,算出下标位置,从table数组中取出Entry
	for (Entry<K, V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) {
			Object k;
			// 查找的key与entry中的key相同,则返回对应的value
			if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
					return e;
	}
	return null;
}

get()方法还是挺简单的,通过key的hash值找到table数组对应下标位置的Entry,然后取出value


其他的方法,看看源码大概都明白了,个人感觉HahsMap最主要的是Entry、hash计算、下标计算(indexFor)


HashMap小结

1. 什么时候会使用HashMap?它有什么特点?
HashMap是基于Map接口的实现,存储键值对时使用。
HashMap可以存储null的键值,是非同步的,HashMap存储着Entry对象

2. HashMap的工作原理
HashMap通过put / get方法存储和获取对象。
存储对象时,我们将kv传给put方法,它调用hashCode计算出hash值,从而得出存储在table数组中的位置,然后进一步存储。
每次存储时,HashMap会根据table数组的容量,自动调整容量(2的幂次方)
如果发生碰撞时,HashMap通过链表将产生碰撞的元素组织起来.

获取对象时,我们将key传给get,它调用hashCode计算hash从而得出key在table数组中的位置,并进一步调用equals方法确定键值对.

3. HashMap的put和get原理,equals和hashCode的作用是什么?
通过key的hashCode(),再计算出hash值,并计算出下标,从而获得在table数组中的位置.
如果产生碰撞,则利用key.equals()方法区链表中查找对应的节点.

4. 如果HashMap的大小超过了负载因子定义的内容,怎么办?
如果超过了负载因子(默认0.75),则会重新resize一个原来长度两倍的HashMap,并重新调用hash方法.


Java中的常见五种map容器源码分析总结(HashMap、Hashtable、ConcurrentHashMap、LinkedHashMap、TreeMap)   断断续续写了半个月,今天终于把TreeMap容器中的红黑树搞定了,现在来水一篇又快又短的Java Map容器总结博客 ????????。 注明:本篇博客不会涉及容器的源码什么的,主要是总结各个map容器的底层实现特点、主要运用场景。如果你需要详细了解更多关于Java中的map容器实现的细节,文末会提供我前面写的源码分析的博客链接,欢迎阅读。 Java中的常见五种map容器源码分析总结目录一、`Java`中... 阅读详情

相关推荐

HashMap源码流程图

HashMap源码流程图 一图解析HashMap源码流程 // 默认的HashMap中数组的长度 16 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 // HashMap中的数组的最大容量 static final int MAXIMUM_CAPACITY = 1 << 30; // 默认的扩容的平衡因子 static final float DEFAULT_LOAD_FACTOR = 0.75f; // 链表转红黑树的 临界值 static final int TREEIFY_THRESHOLD = 8; // 红黑树转链表的 临界值 static final int UNTREEIFY_THRESHOLD = 6 // 链表转红黑树的数组长度的临界值 static final int MIN_TREEIFY_CAPACITY = 64; transient Node<K,V>[] table;// HashMap中的数组结构 transient int size;// HashMap中的元素个数

HashMap深入讲解

摘要: HashMap是Java中基于哈希表的Map实现类,允许null键值且不保证顺序。其底层采用数组+链表/红黑树结构,默认容量16,负载因子0.75(平衡空间与时间效率)。HashSet实际通过HashMap实现。关键特性包括:key需重写equals()和hashCode(),value需重写equals();当链表长度>8且数组容量≥64时转为红黑树。put操作先计算key的hash值(通过hashCode()高16位异或),再处理哈希冲突。构造方法可自定义初始容量和负载因子。

1356

HashMap类

NULL 博文链接:https://wenkaixuan.iteye.com/blog/1075214

HashMap 1.7源码

为什么table数组容量要是2的幂次方? 这样才能用与运算算出数组的下标 为什么算hash的时候要对hashcode进行一系列运算? 让高位参与运算,更加散列,参与数组下标运算的时候,下标更平均 如何控制hashmap不进行扩容? 假设知道要保存的键值对的个数情况下(size),保证阈值>size,就不会扩容;如果不知道size的话,就不能控制。 数组(单纯考虑): 查询快,插入慢 单纯比较的话两种插入性能不一定 ArrayList :add //插入:后面元素会发生后移 链表(单纯考虑

qq_38595536的博客 283

1.6 hashmap源码

hashmap源码,可以看看http://blog.csdn.net/wabiaozia/article/details/50684556

HashMap深度解析[源码]

本文全面剖析了Java中HashMap的工作原理,包括其底层数据结构(数组+链表+红黑树)、核心属性、构造方法及常用操作(插入、获取、删除、扩容)的实现细节。HashMap通过哈希表实现高效的键值对存储,平均时间复杂度为O(1)。文章详细解释了哈希函数的设计、哈希冲突处理(链地址法)、性能分析及使用注意事项,如键的哈希码重写、线程安全问题和初始容量选择。通过代码示例和原理阐述,帮助开发者深入理解HashMap的内部机制,从而在实际应用中优化性能。

Java - 提高-源码(4) - LinkedHashMap

LinkedHashMap源码解析源码解析对应JDK1.7JDK1.7源码下载地址:JDK1.7源码下载JDK 源码注释Hash table and linked list implementation of the Map interface, with predictable iteration order. This implementation differs from HashMap i...

Simba_cheng的博客 555

Hyper-V显卡直通实战:手把手解决Windows 11虚拟机错误码43(附完整参数配置)

本文详细解析了在Windows 11 Hyper-V虚拟机中配置显卡直通时遭遇错误码43的根源,并提供了从环境检查、核心PowerShell命令(如Set-VMGpuPartitionAdapter)配置到虚拟机内驱动替换的完整实战解决方案,帮助用户彻底解决显卡驱动无法加载的问题。

weixin_29238147的博客 898

集合【高级篇】ArrayList、HashMap 、ConcurrentHashMap的底层源码以及相关的扩容机制

文章目录前言一、ArrayList 源码+扩容机制1、ArrayList 简介2、Arraylist 与 LinkedList 区别3、Arraylist核心代码解读4、ArrayList 扩容机制分析二、使用步骤总结 前言   我之前这篇博客 Java高级----List集合、Set集合、Map集合总结 详细介绍了Java中集合相关的基础知识,本篇博客就深入聊聊集合ArrayList、HashMap 、ConcurrentHashMap的底层源码以及相关的扩容机制~ 一、ArrayList 源码+扩容

NanYII_的博客 712

Vscode搭配Keil Assistant插件,头文件红色波浪线终极修复指南

本文详细解析了Vscode搭配Keil Assistant插件时出现头文件红色波浪线问题的根源,即ARMCC与ARMCLANG编译器的兼容性问题,并提供了三种实用解决方案:复制文件夹、创建符号链接和手动配置includePath,帮助开发者彻底修复错误,恢复代码跳转和自动补全功能。

q1w3e5r7t9y的博客 352

Unidbg逆向阿里系App签名:从环境补全到算法还原实战

在移动安全与协议分析领域,理解应用层签名机制是核心挑战之一。签名算法作为保障通信安全与身份验证的关键技术,其原理通常涉及哈希、加密及密钥协商等基础密码学概念。在Android平台,核心签名逻辑常被加固并隐藏于Native层SO库中,以对抗动态调试与静态分析,这为安全研究和协议兼容带来了技术障碍。Unidbg作为一种基于模拟执行的逆向工程工具,通过模拟ARM指令集和系统调用,允许研究者在脱离真机环境的情况下,可控地执行和调试SO文件,为解决此类问题提供了创新思路。其技术价值在于实现了对加固代码的确定性分析与算

weixin_42525738的博客 939

Java 10 大装 B 写法,看完可以出去吹牛逼了

那是因为操作局部变量要比读取全局变量要更快,另外,我个人觉得还有一个好处,再申明一下局部变量,可以很明显的看到这个变量的类型,而不要翻到上面或者用鼠标移上去来看变量类型。没错,你看到的这个关闭流骚操作是 Java 9 的新语法糖,较 Java 7 又简化了try-with-resources用法,装 X 的姿势越来越多了。上面有提到函数式编程,这是 Java 8 里面添加的新特性,我之前在公众号里已经写过很多 Java 新特性的教程,这也不是新玩法了,已经被玩烂了。

the shy 524

特征提取神操作,登上Nature!计算速度狂提300%!

登上Nature!作者提出CVOCA,一种能同时处理数据的幅度和相位信息的卷积模型,在性能远超SOTA的同时,计算速度更是狂飙300%!实际上,作为机器学习和深度学习的核心技术,特征提取一直都是研究的热门!它直接影响着模型的准确性、计算效率、可解释性……同时,它也是我们改模型涨点,最好操作的部分!因为可切入点的真的非常之丰富,不管是轻量化、多尺度、时域与频域结合等,都能使模型性能得到提升!

2401_82426425的博客 4036

Java集合 HashMap 原理解读(含源码解析)

HashMap是Java中基于哈希表实现的Map接口的一个非同步实现类,是以key-value存储形式存在,即主要用来存放键值对。本文系统性梳理和总结了该集合类的几乎所有知识点,进行了源码级的解读。

hrh的博客 1420

西门子 S7-200 SMART 系列六:手把手教你配置S7-200 SMART Modbus RTU

Modbus 是公开通信协议,其最简单的串行通信部分仅规定了在串行线路的基本数据传输格式。Modbus 具有两种串行传输模式,ASCII 和 RTU,它们定义了数据如何打包、解码的不同方式。支持 Modbus 协议的设备一般都支持 RTU 格式。

John_09040211的博客 7431

Vivado工程芯片型号更换全流程:从IP更新到生成新bit文件(避坑指南)

本文详细介绍了Vivado工程中更换芯片型号的全流程,包括IP更新、约束文件适配和生成新bit文件的关键步骤。特别针对FPGA开发中常见的兼容性问题提供避坑指南,帮助开发者高效完成芯片迁移,确保设计稳定性和性能优化。

weixin_42549451的博客 297

HashMap源码讲解

即延迟加载(Lazyload)。简单的说就是只有当我们去调用到它时才会去做加载。

weixin_52112756的博客 2267

点云语义分割:使用Cylinder3D训练SemanticKITTI数据集

将该路径修改到config/semanticktti.yaml文件中的data_path节点下。下载semanticKITTI数据集的点云数据和标签,解压到一起。系统:Ubuntu18。

zhaoguanhua的博客 2597

JAVA之HashMap详解

HashMap 基于哈希表的 Map 接口实现,是以 key-value 存储形式存在,即主要用来存放键值对。HashMap 的实现不是同步的,这意味着它不是线程安全的。它的 key、value 都可以为 null,此外,HashMap 中的映射不是有序的。jdk1.8 之前 HashMap 由数组 + 链表组成,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突(两个对象调用的 hashCode 方法计算的哈希值经哈希函数算出来的地址被别的元素占用)而存在的(“拉链法”解决冲突)。

励志成为一名优秀的全能型软件开发工程师。 2328

Qt 国产嵌入式操作系统实现文字转语音功能(ekho库)

Qt 国产嵌入式操作系统实现文字转语音功能(TTS)_tts驱动 转文字-优快云博客在继上篇文章之后,甲方粑粑不满意使用eSpeak发出的声音太难听了,这就不得不找个替代品,声音稍微好听了一些。使用ekho开源库。Ekho(余音)是一个免费、开源的中文语音合成软件。它目前支持粤语、普通话(国语)、诏安客语、藏语、雅言(中国古代通用语)和韩语(试验中),英文则通过Festival间接实现。Ekho支持Linux、Windows和Android平台。

希望能帮助大家,希望大家多多支持,你们的支持是我前进的动力。 2754

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

图文并茂java源码解析-HashMap

图文并茂java源码解析-HashMap

weixin_46028606的博客 1582

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

Java集合源码系列之HashMap添加元素的流程

一文带你了解HashMap添加元素与扩容的全貌!

tua__的博客 2106

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

HashMap 源码分析

loadFactor 负载因子是控制数组存放数据的疏密程度,loadFactor 越趋近于 1,那么 数组中存放的数据(entry)也就越多,也就越密,也就是会让链表的长度增加,loadFactor 越小,也就是趋近于 0,数组中存放的数据(entry)也就越少,也就越稀疏。判断当前元素存放的位置(这里的 n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。默认的初始化大小为 16。

Wzideng@qq.com 1445

List、HashMap源码

介绍list和hashMap源码

weixin_45104798的博客 1005

java HashMap源码剖析

HashMap 是 Java 集合框架中的一个重要类,它基于哈希表实现,提供了快速的插入、删除和查找操作。 以下是一些关键点: 序列化:HashMap 类实现了 Serializable 接口,这意味着它可以被序列化和反序列化。 初始容量和负载因子:HashMap 有一个初始容量(默认为 16)和一个负载因子(默认为 0.75),这些参数影响哈希表的扩展和收缩行为。 哈希函数:HashMap 使用一个哈希函数来计算键的哈希码,并将其映射到哈希表的索引上。 链表和红黑树:

qq_36070104的博客 744
上一篇: Java - 提高(1) - 抽象类与接口
下一篇: 并发基础_5_并发_ThreadLocal
Simba_cheng
博客等级 码龄11年 250粉丝 203原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值